如何使用$ scope。$ watch和$ scope。$在AngularJS中应用?

我不明白如何使用$scope.$watch$scope.$apply 。 官方文档没有帮助。

具体我不明白:

  • 他们是否连接到DOM?
  • 我如何更新模型的DOM更改?
  • 它们之间的连接点是什么?
  • 我试过这个教程,但它理解$watch$apply是理所当然的。

    $apply$watch做什么,我该如何恰当地使用它们?


    您需要了解AngularJS如何工作才能理解它。

    摘要周期和$范围

    首先,AngularJS定义了一个所谓摘要循环的概念。 这个循环可以被认为是一个循环,在这个循环中AngularJS检查所有$scope 监视的所有变量是否有变化。 因此,如果您的控制器中定义了$scope.myVar并且该变量被标记为被监视 ,那么您隐式地告诉AngularJS在循环的每次迭代中监视myVar上的更改。

    一个自然的后续问题将是:是否所有附加到$scope被监视? 幸运的是,没有。 如果您要监视$scope每个对象的更改,那么快速摘要循环需要很长时间才能评估,并且您很快就会遇到性能问题。 这就是为什么AngularJS团队给了我们两种方法来声明一些$scope变量被监视(阅读下面)。

    $ watch有助于监听$ scope变化

    有两种方法可以将$scope变量声明为被监视。

  • 通过表达式<span>{{myVar}}</span>在模板中使用它
  • 通过$watch服务手动添加它
  • 广告1)这是最常见的情况,我相信你以前也见过它,但你不知道这是在后台创建的。 是的,它已经! 使用AngularJS指令(如ng-repeat )也可以创建隐式手表。

    广告2)这是你如何创建自己的手表$watch服务可以帮助您在附加到$scope某个值发生更改时运行一些代码。 它很少使用,但有时是有帮助的。 例如,如果您想在每次'myVar'更改时运行一些代码,您可以执行以下操作:

    function MyController($scope) {
    
        $scope.myVar = 1;
    
        $scope.$watch('myVar', function() {
            alert('hey, myVar has changed!');
        });
    
        $scope.buttonClicked = function() {
            $scope.myVar = 2; // This will trigger $watch expression to kick in
        };
    }
    

    $ apply允许将更改与摘要循环集成

    您可以像集成机制那样考虑$apply函数 。 您会发现,每次您直接更改附加到$scope对象的某个观察变量时 ,AngularJS都会知道发生了变化。 这是因为AngularJS已经知道要监视这些变化。 因此,如果发生在框架管理的代码中,摘要循环将继续。

    但是,有时候您想要改变AngularJS世界以外的某些值,并看到这些更改正常传播。 考虑一下 - 你有一个$scope.myVar值,它将在jQuery的$.ajax()处理程序中被修改。 这将在未来某个时候发生。 AngularJS不能等待这个事情发生,因为它没有被指示等待jQuery。

    为了解决这个问题,已经引入了$apply 。 它可以让你明确地开始消化循环。 不过,你应该只使用它来将一些数据迁移到AngularJS(与其他框架集成),但从来没有使用这个方法结合常规的AngularJS代码,因为AngularJS会抛出一个错误。

    这与DOM有什么关系?

    那么,你应该再次遵循教程,现在你知道这一切。 摘要循环将确保UI和JavaScript代码保持同步,只要没有任何变化,就评估附加到所有$scope的每个观察者。 如果在摘要循环中没有发生更多变化,则认为它已完成。

    您可以直接在视图中将对象附加到$scope对象,或者直接在{{expression}}表单中声明它们。

    我希望这有助于澄清关于这一切的一些基本知识。

    更多阅读材料:

  • 制作你自己的AngularJS,第1部分:范围和摘要

  • 在AngularJS中,我们更新模型,并且我们的视图/模板“自动”更新DOM(通过内置或自定义指令)。

    $ apply和$ watch都是Scope方法,都与DOM无关。

    概念页面(“运行时”部分)对$ digest循环$ apply,$ evalAsync队列和$ watch列表有很好的解释。 这是伴随文本的图片:

    $ digest循环

    无论代码如何访问范围 - 通常是控制器和指令(它们的链接函数和/或其控制器)都可以设置一个“watchExpression”,以便AngularJS根据该范围进行评估。 只要AngularJS进入$ digest循环(特别是“$ watch list”循环),就会进行评估。 您可以观察单个范围属性,您可以定义一个函数来一起观察两个属性,您可以观察数组的长度等。

    当事件发生在“AngularJS内部”时 - 例如,你输入一个启用了AngularJS双向数据绑定的文本框(例如,使用ng-model),$ http回调触发等 - $ apply已经被调用,所以我们'在上图中的“AngularJS”矩形内。 所有watchExpressions将被评估(可能不止一次 - 直到没有检测到进一步的变化)。

    当事情发生在“AngularJS之外”时 - 例如,您在指令中使用了bind(),然后该事件触发,导致您的回调被调用,或者一些jQuery注册的回调触发 - 我们仍然在“Native”矩形中。 如果回调代码修改任何$ watch观看的内容,则调用$ apply来进入AngularJS矩形,导致$ digest循环运行,因此AngularJS会注意到这一变化并发挥它的魔力。


    这篇博客已经涵盖了所有的创建例子和可理解的解释。

    AngularJS $scope函数$watch(), $digest()$apply()是AngularJS中的一些中心函数。 为了理解AngularJS,理解$watch()$digest()$apply()是必不可少的。

    当您从视图中的某处创建数据绑定到$ scope对象上的变量时,AngularJS将在内部创建一个“手表”。 手表意味着AngularJS监视$scope object上变量的变化。 该框架正在“观察”变量。 手表是使用$scope.$watch()函数创建的,我将在本文后面介绍。

    在应用程序的关键点AngularJS调用$scope.$digest()函数。 此函数遍历所有监视并检查是否有任何监视的变量已更改。 如果观察变量已更改,则会调用相应的侦听器函数。 监听器函数可以完成它需要做的任何工作,例如更改HTML文本以反映监视变量的新值。 因此, $digest()函数是触发数据绑定更新的东西。

    大部分时间AngularJS都会调用$ scope。$ watch()和$scope.$digest()函数,但在某些情况下,您可能需要自己调用它们。 因此,了解他们的工作方式真的很好。

    $scope.$apply()函数用于执行某些代码,然后调用$scope.$digest() ,以便检查所有手表,并调用相应的监听器函数。 当将AngularJS与其他代码集成时, $apply()函数非常有用。

    我将在本文的其余部分详细介绍$watch(), $digest()$apply()函数。

    $表()

    $scope.watch()函数创建一些变量的监视。 当您注册手表时,您将两个函数作为参数传递给$watch()函数:

  • 值函数
  • 侦听器功能
  • 这里是一个例子:

    $scope.$watch(function() {},
                  function() {}
                 );
    

    第一个函数是值函数,第二个函数是侦听器函数。

    值函数应该返回正在观察的值。 然后AngularJS可以检查返回的值与watch函数上一次返回的值。 这样AngularJS可以确定这个值是否已经改变。 这里是一个例子:

    $scope.$watch(function(scope) { return scope.data.myVar },
                  function() {}
                 );
    

    这个示例的值函数返回$scope变量scope.data.myVar 。 如果此变量的值发生变化,将返回一个不同的值,并且AngularJS将调用监听器函数。

    注意值函数如何将范围作为参数(名称中没有$)。 通过这个参数,值函数可以访问$scope和它的变量。 如果需要,值函数也可以观察全局变量,但大多数情况下您会观察$scope变量。

    如果值已更改,监听器函数应该做任何需要做的事情。 也许你需要改变另一个变量的内容,或者设置HTML元素的内容或其他东西。 这里是一个例子:

    $scope.$watch(function(scope) { return scope.data.myVar },
                  function(newValue, oldValue) {
                      document.getElementById("").innerHTML =
                          "" + newValue + "";
                  }
                 );
    

    本示例将HTML元素的内部HTML设置为嵌入到b元素中的变量的新值,该元素使值为粗体。 当然,您可以使用代码{{ data.myVar }完成此操作,但这只是您可以在侦听器函数内部执行的一个示例。

    $摘要()

    $scope.$digest()函数迭代$scope object中的所有监视,以及它的子$ scope对象(如果有的话)。 当$digest()遍历手表时,它会调用每个手表的值函数。 如果value函数返回的值不同于上次调用该函数返回的值,那么将调用该表的侦听器函数。

    每当AngularJS认为有必要的时候,都会调用$digest()函数。 例如,在执行按钮点击处理程序之后,或者在AJAX调用返回后(执行done()/ fail()回调函数之后)。

    您可能会遇到一些AngularJS不会为您调用$digest()函数的情况。 通常您会发现数据绑定不会更新显示的值。 在这种情况下,调用$scope.$digest() ,它应该可以工作。 或者,你可以使用$scope.$apply()来代替,我将在下一节中解释它。

    $适用()

    $scope.$apply()函数将一个函数作为参数执行,并在$scope.$digest()被内部调用。 这使您更容易确保所有手表都已被检查,从而刷新所有数据绑定。 这是一个$apply()示例:

    $scope.$apply(function() {
        $scope.data.myVar = "Another value";
    });
    

    传递给$apply()函数作为参数的函数将改变$scope.data.myVar的值。 当函数退出时,AngularJS将调用$scope.$digest()函数,以便检查所有手表的观察值的变化。

    为了说明$watch()$digest( )和$apply()工作的,请看下面的例子:

    <div ng-controller="myController">
        {{data.time}}
    
        <br/>
        <button ng-click="updateTime()">update time - ng-click</button>
        <button id="updateTimeButton"  >update time</button>
    </div>
    
    
    <script>
        var module       = angular.module("myapp", []);
        var myController1 = module.controller("myController", function($scope) {
    
            $scope.data = { time : new Date() };
    
            $scope.updateTime = function() {
                $scope.data.time = new Date();
            }
    
            document.getElementById("updateTimeButton")
                    .addEventListener('click', function() {
                console.log("update time clicked");
                $scope.data.time = new Date();
            });
        });
    </script>
    

    他的示例将$scope.data.time变量绑定到一个插值指令,该指令将变量值合并到HTML页面中。 该绑定在$scope.data.time variable内部创建一个监视。

    该示例还包含两个按钮。 第一个按钮有一个ng-click监听器。 点击该按钮后,将调用$scope.updateTime()函数,然后AngularJS调用$scope.$digest()以便更新数据绑定。

    第二个按钮从控制器函数内部获得一个标准的JavaScript事件监听器。 当点击第二个按钮时,执行侦听器功能。 正如你所看到的,两个按钮的侦听器函数几乎完全相同,但是当第二个按钮的侦听器函数被调用时,数据绑定不会被更新。 这是因为$scope.$digest()在第二个按钮的事件侦听器执行后没有被调用。 因此,如果您单击第二个按钮,则会在$scope.data.time变量中更新时间,但不会显示新时间。

    为了解决这个问题,我们可以添加一个$scope.$digest()调用到按钮事件监听器的最后一行,如下所示:

    document.getElementById("updateTimeButton")
            .addEventListener('click', function() {
        console.log("update time clicked");
        $scope.data.time = new Date();
        $scope.$digest();
    });
    

    而不是在按钮侦听器函数内部调用$digest() ,也可以像这样使用$apply()函数:

    document.getElementById("updateTimeButton")
            .addEventListener('click', function() {
        $scope.$apply(function() {
            console.log("update time clicked");
            $scope.data.time = new Date();
        });
    });
    

    请注意, $scope.$apply()函数是如何从按钮事件侦听器中调用的,以及$scope.data.time变量的更新如何在作为参数传递给$apply()函数的函数中执行。 当$apply()函数调用完成时,AngularJS会在内部调用$digest() ,以便更新所有数据绑定。

    链接地址: http://www.djcxy.com/p/13587.html

    上一篇: How do I use $scope.$watch and $scope.$apply in AngularJS?

    下一篇: How do I access the $scope variable in browser's console using AngularJS?