【问题标题】:$watch is firing only once inside directive$watch 在指令内只触发一次
【发布时间】:2015-04-17 19:06:11
【问题描述】:

我是 Angular js 的新手,所以请有人帮帮我。我在这里有我的模板:

<form ng-model="signup" form-valid>
    <input type="text" name="username" ng-model="signup.username">{{signup}}
</form>

我的指令是这样的:

app.directive("formValid",function(){

 return {
    restrict:"A",
    require:"ngModel",
    link:function(scope,element,attrs){
          scope.$watch(attrs.ngModel,function(newValue){
              if(newValue){
                 console.log(newValue);
              }
          });
       }
   }});

当我在文本框中输入一些值时,模型正在改变,因此应该触发“$watch”。但是在这里,当我第一次在文本框中输入任何值时,“$watch”只会触发一次.提前致谢。

【问题讨论】:

  • 您可能想查看模型的属性,而不仅仅是对象本身:scope.$watch(attrs.ngModel, {…}, true);
  • 它对我有用。第三个参数“true”在做什么。以前没有“true”,它只触发一次。
  • 第三个参数是看“深”与否。例如检查其相同的对象或检查对象具有所有相同的键/值

标签: angularjs


【解决方案1】:

当您使用ngModelController 时,观察模型变化的标准方法是创建一个formatter

link: function(scope, element, attrs, ngModelCtrl) {
    ngModelCtrl.$formatters.push(function(value) {
        // Do something with value
        return value;
    });
}

请记住,格式化程序仅在直接更改模型时才会触发。如果更改来自 UI(即用户更改了某些内容),则会触发 解析器。所以你可能也需要这样做:

ngModelCtrl.$parsers.push(function(value) {
    // Do something with value
    return value;
});

Working Plunker

我建议您阅读ngModelController 文档,以便准确了解这些管道的工作原理。

但是,如果您只想在模型更改时收到通知(您不希望或不需要格式化或解析任何内容),那么您可以做一些更简单的事情:

scope: { model: '=ngModel' },
link: function(scope) {
    scope.$watch('model', function(value) {
        // Do something with value
    });
}

Working Plunker

但鉴于您的指令名称formValid,我认为使用 ngModelController 是正确的方法。

更新

值得一提的是,ngModelController 可以说有一个缺点:它不能很好地处理“引用”类型(例如数组、对象)。更多详情请见here

【讨论】:

  • 谢谢迈克尔。但有一件事我想知道。当我向观察者添加第三个参数为“true”时,它开始观察模型。但以前为什么它只触发一次?下面的代码适用于我 scope.$watch('model', function(value) { // Do something with value },true);
  • @Nitya 第三个参数告诉 Angular 使用 angular.equals 来比较值(即它进行深度比较)。因此,如果您需要监视对象并在其属性之一发生更改时收到通知,则需要设置该参数(以换取一些性能)。这是ngModelController 的一个缺点:它不进行这种比较,因此更适合“值类型”。
  • @Nitya 关于手表只触发一次,这是意料之中的。每个监视至少运行一次(如果被监视的属性没有值,则使用未定义的参数),如您在this plunker 中所见。
【解决方案2】:

一种解决方案是使用 ngModel,如下例所示。

app.directive("formValid",function(){
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, element, attrs, ngModel) {
      scope.$watch(function () {
          return ngModel.$modelValue;
        }, function(newValue) {
          console.log(newValue);
        });
      }
    }
  });

编辑:最快的解决方案是关注 Blackhole 的评论,并通过添加 true 将您的手表更新为深度手表。这是因为您正在查看 signup 属性,但模型值 username 是注册的属性。 deep watch 将用于监视属性。

直接使用ngModel,而不是通过attrs,或使用格式化程序或解析器(Michaels 的回答中详述,我认为这是一个更好的解决方案)。

【讨论】:

  • OP在看attrs.ngModel的内容,即signup,是继承的作用域变量。
  • 是的,很公平,我试图在链接函数中区分使用作用域和属性,以避免与继承作用域混淆。我会更新我的答案,但迈克尔提供了一个很好的解决方案。谢谢。
  • 如果其中一个满足您的问题要求,请记住接受答案。
猜你喜欢
  • 2015-09-07
  • 2016-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多