在解释ngModel管道的顺序,解析器,格式化,viewChangeListeners和$观察家 [英] Explaining the order of the ngModel pipeline, parsers, formatters, viewChangeListeners, and $watchers

查看:193
本文介绍了在解释ngModel管道的顺序,解析器,格式化,viewChangeListeners和$观察家的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这并不容易帧这个问题,所以我会尽量解释我想知道的一个例子是什么:

It's not easy to frame this question, so I will try to explain what I want to know with an example:

考虑一个简单的angularjs 应用 PLUNKER

Consider this simple angularjs app: PLUNKER

angular.module('testApp', [])
.controller('mainCtrl', function($scope) {
  $scope.isChecked = false;
})
.directive("testDirective", function () {
    return {
        restrict: 'E',
        scope: {
            isChecked: '='
        },
        template: '<label><input type="checkbox" ng-model="isChecked" /> Is it Checked?</label>'+
                  '<p>In the <b>directive\'s</b> scope <b>{{isChecked?"it\'s checked":"it isn\'t checked"}}</b>.</p>'
    };
}); 

通过这个网站:

  <body ng-controller="mainCtrl">
    <test-directive is-checked="isChecked"></test-directive>
    <p>In the <b>controller's</b> scope <b>{{isChecked?"it\'s checked":"it isn\'t checked"}}</b>.</p>
  </body>

该应用程序:


  • 有一个所谓的控制器:mainCtrl在这里我们定义了一个名为器isChecked作用域变量

  • 它也有称为testDirective与一个孤立的范围某个指令指向和所谓的器isChecked绑定属性

  • 而在HTML我们实例化testDirective中的mainCtrl内和有约束力的器isChecked的mainCtrl作用域属性与器isChecked的指令的隔离范围的属性。

  • 该指令使得它有一个复选框器isCheckedscope属性的一种模式。

  • 当我们选中或取消选中,我们可以看到,无论作用域属性都同时更新的复选框。

到目前为止,一切都很好。

So far, so good.

现在,让我们稍作改动,这样的: PLUNKER

Now let's make a little change, like this: PLUNKER

angular.module('testApp', [])
.controller('mainCtrl', function($scope) {
  $scope.isChecked = false;
  $scope.doingSomething = function(){alert("In the controller's scope is " + ($scope.isChecked?"checked!":"not checked"))};
})
.directive("testDirective", function () {
    return {
        restrict: 'E',
        scope: {
            isChecked: '=',
            doSomething: '&'
        },
        template: '<label><input type="checkbox" ng-change="doSomething()" ng-model="isChecked" /> Is it Checked?</label>'+
                  '<p>In the <b>directive\'s</b> scope <b>{{isChecked?"it\'s checked":"it isn\'t checked"}}</b>.</p>'
    };
}); 

和这样的:

<!DOCTYPE html>
<html ng-app="testApp">
  <head>
    <script data-require="angular.js@1.3.0-beta.5" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>
  <body ng-controller="mainCtrl">
    <test-directive is-checked="isChecked" do-something="doingSomething()"></test-directive>
    <p>In the <b>controller's</b> scope <b>{{isChecked?"it\'s checked":"it isn\'t checked"}}</b>.</p>
  </body>
</html>

这是我们所做的唯一事情是:

The only thing that we have done is:


  • 在做控制器的范围定义函数的 window.alert 指示是否控制器的范围的器isChecked'属性被选中或取消选中。 (我做了 window.alert 故意的,因为我想停止执行)

  • 绑定该功能进入指令

  • 在该指令触发该功能的复选框的NG变化。

  • Define a function in the scope of the controller that does a window.alert indicating if the 'isChecked' attribute of the controller's scope is checked or unchecked. (I'm doing a window.alert on purpose, because I want the execution to stop)
  • Bind that function into the directive
  • In the "ng-change" of the checkbox of the directive trigger that function.

现在,当我们选中或取消选中我们得到警报的复选框,然后在警告我们可以看到,该指令的范围尚未更新。好了,人们会认为 NG-变化被触发模型被更新之前,也正在显示警报,我们可以,根据在渲染文本见浏览器器isChecked具有两个范围的值相同。好吧,没什么大不了的,如果是这样的NG-变的行为,这样吧,我们总是可以设置一个 $观看和运行功能有...但是让我们做另一项实验:

Now, when we check or uncheck the checkbox we get an alert, and in that alert we can see that the scope of the directive hasn't been updated yet. Ok, so one would think that the ng-change gets triggered before the model gets updated, also while the alert is being displayed we can see that according to the text rendered in the browser "isChecked" has the same value in both scopes. All right, no big deal, if that's how the "ng-change" behave, so be it, we can always set a $watch and run the function there... But lets do another experiment:

这样的: PLUNKER

.directive("testDirective", function () {
    return {
        restrict: 'E',
        scope: {
            isChecked: '=',
            doSomething: '&'
        },
        controller: function($scope){
          $scope.internalDoSomething = function(){alert("In the directive's scope is " + ($scope.isChecked?"checked!":"not checked"))};
        },
        template: '<label><input type="checkbox" ng-change="internalDoSomething()" ng-model="isChecked" /> Is it Checked?</label>'+
                  '<p>In the <b>directive\'s</b> scope <b>{{isChecked?"it\'s checked":"it isn\'t checked"}}</b>.</p>'
    };
}); 

现在,我们只是使用指令范围的功能做到这一点的控制器范围的功能,在做同样的事情,但这次事实证明,该模型已被更新,所以它似乎在这一点上该指令的​​范围被更新,但不更新控制器的范围...怪异!

Now we are just using a function of the scope of the directive to do the same thing that the function of the scope of the controller was doing, but this time it turns out that the model has been updated, so it seems that at this point the scope of the directive is updated but the scope of the controller is not updated... Weird!

让我们确保是这样的话: PLUNKER

Let's make sure that that's the case: PLUNKER

angular.module('testApp', [])
.controller('mainCtrl', function($scope) {
  $scope.isChecked = false;
  $scope.doingSomething = function(directiveIsChecked){
    alert("In the controller's scope is " + ($scope.isChecked?"checked!":"not checked") + "\n"
        + "In the directive's scope is " + (directiveIsChecked?"checked!":"not checked") );
  };
})
.directive("testDirective", function () {
    return {
        restrict: 'E',
        scope: {
            isChecked: '=',
            doSomething: '&'
        },
        controller: function($scope){
          $scope.internalDoSomething = function(){ $scope.doSomething({directiveIsChecked:$scope.isChecked}) }; 
        },
        template: '<label><input type="checkbox" ng-change="internalDoSomething()" ng-model="isChecked" /> Is it Checked?</label>'+
                  '<p>In the <b>directive\'s</b> scope <b>{{isChecked?"it\'s checked":"it isn\'t checked"}}</b>.</p>'
    };
}); 

这一次,我们正在使用的指令范围的功能,触发控制器的约束功能,我们传递一个参数传递给控制器​​的功能与指令的范围值。现在,在控制器的功能,我们可以证实了我们在previous一步,这已经是怀疑:该分离范围首先得到更新,那么 NG-变化变触发,它不是在那之后,该指令的范围的绑定得到更新。

This time we are using the function of the scope of the directive to trigger the bound function of the controller, and we are passing an argument to the controller's function with the value of the directive's scope. Now in the controller's function we can confirm what we already suspected in the previous step, which is: that the isolated scope gets updated first, then the ng-change gets triggered, and it's not after that, that the bindings of the directive's scope get updated.

现在,终于,我的问题/ S:

Now, finally, my question/s:


  • 不应该angularjs更新在同一时间的所有绑定属性,在做任何事情之前?

  • 谁能给我什么内部发生的事情,为了证明这种行为的详细说明?

在换句话说:如果NG-变得到了触发模型被更新之前,我能理解,但我有一个非常很难理解,一个函数被更新模型后整理之前触发填充绑定属性的变化。

In other words: if the "ng-change" got triggered before the model gets updated, I could understand that, but I'm having a very hard time understanding that a function gets triggered after updating the model and before finishing to populate the changes of the bound properties.

如果你读到这里!祝贺和感谢您的耐心

If you read this far: congratulations and thanks for your patience!

何塞普

推荐答案

要总结问题, ngModelController 有一个过程要经过之前手表将被解雇。你登录外 $范围前财产 ngModelController 已处理的变化,引起了$消化周期,这将在转火 $观察家。我不会考虑的模式更新,直到这一点。

To summarize the problem, ngModelController has a process to go through before watches will be fired. You're logging the outer $scope property before ngModelController has processed the change and caused a $digest cycle, which would in turn fire $watchers. I wouldn't consider the model updated until that point.

这是一个复杂的系统。我做这个演示作为参考。我建议改变了收益值,打字,和点击 - 只是梅辛与它周围的各种方式和检查日志。这使得它非常快速清除一切是如何工作的。

This is a complex system. I made this demo as a reference. I recommend changing the return values, typing, and clicking - just messing around with it in all kinds of ways and checking the log. This makes it clear very quickly how everything works.

ngModelController 都有它自己的功能阵列,以不同的变化的反应上运行。

ngModelController has it's own arrays of functions to run as responses to different changes.

ngModelController 有两种管道的决定如何处理一个样的变化做。这些允许开发以控制值的流动。

ngModelController has two kinds of "pipelines" for determining what to do with a kind of change. These allow the developer to control the flow of values.

如果指定为 ngModel 的变化范围属性,则 $格式管道将运行。这条管道是用来确定如何从 $范围来的值应在视图中显示,但叶单模式。因此, NG-模式=富 $ scope.foo ='123',通常会显示 123 输入,但格式可以返回 1-2-3 或任何价值。 $ scope.foo 仍是123,但它显示为无论格式化返回。

If the scope property assigned as ngModel changes, the $formatter pipeline will run. This pipeline is used to determine how the value coming from $scope should be displayed in the view, but leaves the model alone. So, ng-model="foo" and $scope.foo = '123', would typically display 123 in the input, but the formatter could return 1-2-3 or any value. $scope.foo is still 123, but it is displayed as whatever the formatter returned.

$解析器处理同样的事情,但在相反。当用户键入的东西,在$解析器管道运行。无论一个 $解析器的回报是什么将被设置为 ngModel。$ modelValue 。因此,如果用户键入 ABC $解析器收益 ABC ,那么该视图将不会改变,但 $ scope.foo 现在 ABC

$parsers deal with the same thing, but in reverse. When the user types something, the $parser pipeline is run. Whatever a $parser returns is what will be set to ngModel.$modelValue. So, if the user types abc and the $parser returns a-b-c, then the view won't change, but $scope.foo now is a-b-c.

在任何一个 $格式 $解析器运行, $验证将运行。无论属性名用于验证的有效性将通过验证函数(的返回值设置为真正)。

After either a $formatter or $parser runs, $validators will be run. The validity of whatever property name is used for the validator will be set by the return value of the validation function (true or false).

$ viewChangeListeners 被解雇视图更改后,没有模型更改。因为我们指的是 $ scope.foo 和NOT ngModel。$ modelValue 这一个是特别令人困惑。视图将不可避免地更新 ngModel。$ modelValue (除非在管道pvented $ P $),但事实并非模式的变革我们指的是。基本上, $ viewChangeListeners $解析器后被解雇和NOT后 $格式化。所以,当视图价值变动(用户类型), $解析器,$验证,那么$ viewChangeListeners 。乐倍= D

$viewChangeListeners are fired after view changes, not model changes. This one is especially confusing because we're referring to $scope.foo and NOT ngModel.$modelValue. A view will inevitably update ngModel.$modelValue (unless prevented in the pipeline), but that is not the model change we're referring to. Basically, $viewChangeListeners are fired after $parsers and NOT after $formatters. So, when the view value changes (user types), $parsers, $validators, then $viewChangeListeners. Fun times =D

这一切都从 ngModelController 内部发生。在这个过程中, ngModel 对象不更新像你所期望的。管道被绕过,这将影响该对象的值。在该过程结束时, ngModel 的对象将用适当的 $ viewValue $ modelValue 。

All of this happens internally from ngModelController. During the process, the ngModel object is not updated like you might expect. The pipeline is passing around values that will affect that object. At the end of the process, the ngModel object will be updated with the proper $viewValue and $modelValue.

最后, ngModelController 完成和 $消化周期将发生允许应用程序的其余部分响应所产生的变化。

Finally, the ngModelController is done and a $digest cycle will occur to allow the rest of the application to respond to the resulting changes.

下面是在什么情况下,演示了code应该发生的:

Here's the code from the demo in case anything should happen to it:

<form name="form">
  <input type="text" name="foo" ng-model="foo" my-directive>
</form>
<button ng-click="changeModel()">Change Model</button>
<p>$scope.foo = {{foo}}</p>
<p>Valid: {{!form.foo.$error.test}}</p>

JS:

angular.module('myApp', [])

.controller('myCtrl', function($scope) {

  $scope.foo = '123';
  console.log('------ MODEL CHANGED ($scope.foo = "123") ------');

  $scope.changeModel = function() {
    $scope.foo = 'abc';
    console.log('------ MODEL CHANGED ($scope.foo = "abc") ------');
  };

})

.directive('myDirective', function() {
  var directive = {
    require: 'ngModel',
    link: function($scope, $elememt, $attrs, $ngModel) {

      $ngModel.$formatters.unshift(function(modelVal) {
        console.log('-- Formatter --', JSON.stringify({
          modelVal:modelVal,
          ngModel: {
            viewVal: $ngModel.$viewValue,
            modelVal: $ngModel.$modelValue
          }
        }, null, 2))
        return modelVal;
      });

      $ngModel.$validators.test = function(modelVal, viewVal) {
        console.log('-- Validator --', JSON.stringify({
          modelVal:modelVal,
          viewVal:viewVal,
          ngModel: {
            viewVal: $ngModel.$viewValue,
            modelVal: $ngModel.$modelValue
          }
        }, null, 2))
        return true;
      };

      $ngModel.$parsers.unshift(function(inputVal) {
        console.log('------ VIEW VALUE CHANGED (user typed in input)------');
        console.log('-- Parser --', JSON.stringify({
          inputVal:inputVal,
          ngModel: {
            viewVal: $ngModel.$viewValue,
            modelVal: $ngModel.$modelValue
          }
        }, null, 2))
        return inputVal;
      });

      $ngModel.$viewChangeListeners.push(function() {
        console.log('-- viewChangeListener --', JSON.stringify({
          ngModel: {
            viewVal: $ngModel.$viewValue,
            modelVal: $ngModel.$modelValue
          }
        }, null, 2))
      });

      // same as $watch('foo')
      $scope.$watch(function() {
        return $ngModel.$viewValue;
      }, function(newVal) {
        console.log('-- $watch "foo" --', JSON.stringify({
          newVal:newVal,
          ngModel: {
            viewVal: $ngModel.$viewValue,
            modelVal: $ngModel.$modelValue
          }
        }, null, 2))
      });


    }
  };

  return directive;
})

;

这篇关于在解释ngModel管道的顺序,解析器,格式化,viewChangeListeners和$观察家的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆