Пользовательская проверка по AngularJS не срабатывает при программном изменении модели

Я создал специальный валидатор, требующий, чтобы дата была в прошлом. Валидация, похоже, отлично работает при вводе даты вручную в поле. Однако, если я вводю изменения даты программным путем (измените модель напрямую, а не набирать в поле), проверка не срабатывает.

Я считаю, что я выполняю собственную директиву проверки, как указано в документации. Вот jsFiddle, иллюстрирующий проблему. В скрипке, если вы нажмете кнопку "Изменить дату программно", вы увидите, что ошибка проверки не отображается (но это происходит, если вы меняете ее вручную). Вот код директивы (также в скрипке):

myApp.directive('pastDate', function() {
    return {
        restrict: 'A',
        require: '?ngModel',
        link: function (scope, element, attrs, ctrl) {
            ctrl.$parsers.unshift(function (viewValue) {
                var today = new Date();
                today = new Date(today.getFullYear(), today.getMonth(), today.getDate());

                if (new Date(viewValue) < today) {
                    ctrl.$setValidity('pastDate', true);
                    return viewValue;
                }
                ctrl.$setValidity('pastDate', false);
                return undefined;
            });
        }
    };
});

Ответы

Ответ 1

Существует два способа привязки модели, $parsers управляет конвейером направления "вид-модель", а $formatters управляет конвейером направления модели для просмотра. Когда вы обновляете модель в контроллере, изменение проходит через конвейер $formatters.

Я обновил ваш код до: this, поэтому он обрабатывает оба способа.

myApp.directive('pastDate', function() {
    return {
        restrict: 'A',
        require: '?ngModel',
        link: function (scope, element, attrs, ctrl) {
            function validate (value) {
                var today = new Date();
                today = new Date(today.getFullYear(), today.getMonth(), today.getDate());

                if (new Date(value) < today) {
                    ctrl.$setValidity('pastDate', true);
                    return value;
                }
                ctrl.$setValidity('pastDate', false);
                return value;
            }
            ctrl.$parsers.unshift(validate);
            ctrl.$formatters.unshift(validate)
        }
    };
});

Ответ 2

Новый ответ с angular 1.3 предоставляет свойство $validators.

Так как 1.3, $parsers и $formatters больше не должны устанавливать силу, даже если это возможно.

Затем ваш код станет проще:

myApp.directive('pastDate', function() {
    return {
        restrict: 'A',
        require: '?ngModel',
        link: function (scope, element, attrs, ctrl) {
            ctrl.$validators.pastDate = function(modelValue) { // 'pastDate' is the name of your custom validator ...
                var today = new Date();
                today = new Date(today.getFullYear(), today.getMonth(), today.getDate());
                return (new Date(modelValue) < today);
            }
        }
    };
});

Обновлен jsFiddle: http://jsfiddle.net/jD929/53/