Проблемы с привязкой Angularjs, когда ввод имеет директиву с областью действия

Я создал пользовательскую директиву в AngularJS. Директива использует изолированную область видимости и каким-то образом предотвращает привязку стандартной ngModel к тому же элементу. Я хочу создать поле подтверждения пароля (текст для удобочитаемости в примере).

<input type="text" name="one" ng-model="fields.field_one">
<input type="text" validate-match="fields.field_one" name="two" ng-model="field_two">

Моя директива аннулирует поле, если совпадения нет.

app.directive('validateMatch', function() {
  return {
    require: 'ngModel',
    scope: { matchValue: '=validateMatch' },
    link: function(scope, elm, attr, ctrl) {
      scope.$watch('matchValue', function(value) {
        ctrl.$setValidity('match', 
            ctrl.$viewValue === value
            || !ctrl.$viewValue && !value);
        });

      function validate(value) {
        ctrl.$setValidity('match', value === scope.matchValue);
        return value;
      }
      ctrl.$parsers.push(validate);
      ctrl.$formatters.push(validate);
    }
  }
});

Дело в том, почему я не могу изменить значение этого поля, изменив модель? Первое поле работает нормально.

Посмотрите plunker, чтобы узнать подробности и прокомментировать код.


person Frizi    schedule 03.08.2013    source источник
comment
Изолированные области и ng-модель плохо сочетаются: stackoverflow.com/questions/11896732/   -  person Mark Rajcok    schedule 04.08.2013
comment
@MarkRajcok, спасибо, мне удалось это сделать с вашей помощью.   -  person Frizi    schedule 04.08.2013


Ответы (3)


Как упоминалось в комментарии, изолированные области и ng-модель плохо сочетаются. Кроме того, здесь не следует использовать изолированную область, поскольку мы пытаемся создать директиву/компонент, который должен взаимодействовать с другой директивой (в данном случае ng-model).

Поскольку директива validateMatch не создает никаких новых свойств, директиве не нужно создавать новую область действия. $parse можно использовать для получения значения свойства, на которое ссылается атрибут validate-match:

app.directive('validateMatch', function($parse) {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, elm, attr, ctrl) {
      var model = $parse(attr.validateMatch);

      // watch for linked field change (field_one)
      scope.$watch(model, function(value) {
        console.log('linked change:', value, ctrl.$viewValue);
        // set valid if equal or both falsy (empty/undefined/null)
        ctrl.$setValidity('match', 
            ctrl.$viewValue === value
            || !ctrl.$viewValue && !value);
      });

      // validate on parse/format (field_two)
      function validate(value) {
        var otherFieldValue = model(scope);
        console.log('validate:', value, otherFieldValue);
        // set valid if equal
        ctrl.$setValidity('match', value === otherFieldValue);
        return value;
      }

      ctrl.$parsers.push(validate);
      ctrl.$formatters.push(validate);
    }
  };
});

плункер

person Mark Rajcok    schedule 05.08.2013
comment
Это похоже на правильный способ сделать это без каких-либо хаков. Спасибо. - person Frizi; 05.08.2013

Следуя предложению Марка, мне удалось найти обходной путь.

Когда для элемента существует изолированная область, ngModel ссылается на него. Хитрость заключается в том, чтобы посмотреть на родительскую область изнутри. Вы можете либо вручную изменить ngModel (добавив к нему $parent.), либо автоматизировать этот процесс внутри директивы с помощью соответствующей функции компиляции.

Вот как я это сделал:

compile: function(element, attrs, transclude) {
    // reference parent scope, because isolated
    // scopes are not looking up by default
    attrs.$set('ngModel', '$parent.'+attrs.ngModel, false);

    return function(scope, elm, attr, ctrl) {
        // link function body there
    }
}

Полный пример см. в этом фрагменте.

person Frizi    schedule 04.08.2013

Насколько я понимаю директивы AngularJS, вы можете использовать transclude для доступа к родительской области контроллера.

person Bazmo    schedule 31.10.2013
comment
Насколько я понимаю, transclude передает область действия директивы в ее содержимое и все дочерние директивы внутри нее. - person Frizi; 07.11.2013