为什么 Angular 没有注意到这个变量的值发生了变化

Why doesn't Angular notice that this variable's value has changed?

本文关键字:变量 发生了 变化 Angular 注意到 为什么      更新时间:2023-09-26

我在一个非常简单的Angular应用程序上遇到了问题。这是一个代码笔;下面有相同的代码。

在这个应用程序中,你点击一个按钮吃香蕉,你吃过的香蕉总数会显示在页面上。每次点击后,应该有一秒钟的冷却时间,然后才能吃另一根香蕉。

为此,我做了以下工作:

  • $scope.cooling_down是一个布尔值。

  • $scope.can_eat_banana是一个返回truefalse的函数,指示"吃香蕉"按钮是否应该可用。基本上,如果$scope.cooling_down为假,则返回 true。

  • "吃香蕉"按钮有ng-disabled="!can_eat_banana()"

  • 当用户点击"吃香蕉"按钮时,我将$scope.cooling_down设置为true,并使用window.setTimeout()安排回调以将其设置为一秒后false

(你可能会问:为什么不完全摆脱$scope.can_eat_banana(),而简单地使用按钮上的ng-disabled="cooling_down"?答:因为,随着游戏的发展,决定玩家是否可以吃香蕉的逻辑会变得更加复杂。把这个逻辑放在视图中似乎真的很混乱。

因此,当我运行此测试用例时,会发生以下情况:

  1. 我加载页面。最初,该按钮处于启用状态。

  2. 我点击"吃香蕉"按钮。正如预期的那样,我的代码将$scope.cooling_down设置为 true ,并且按钮被禁用。目前为止,一切都好。

  3. 一秒钟后,我传递到window.setTimeout()的回调触发了。(我有一个console.log()作为回调的第一行,以确认它正在运行。此回调将$scope.cooling_down设置回 false 。(您可以查看控制台以确认值已更新。但:Angular 没有注意到该值已更改。该按钮不会重新启用,并且页面上的{{cooling_down}}令牌将卡在true上。

为什么?为什么 Angular 在第一次更改值时(当用户单击按钮后将其更改为 true 时)注意到,但第二次(当回调触发并将其设置回false时)没有注意到?

<!DOCTYPE html>
<html ng-app="game">
<head>
    <meta charset="utf-8">
    <title>Incredibly Stupid Banana Game</title>
</head>
<body ng-controller="GameController">
    <p>You have eaten {{bananas_eaten}} bananas.</p>
    <button type="button" ng-click="eat_banana()" ng-disabled="!can_eat_banana()">Eat a Banana</button>
    <p>[current value of cooling_down: {{cooling_down}}]</p>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.min.js"></script>
    <script>
        ( function() {
            var app = angular.module( "game", [] );
            app.controller( "GameController", [ "$scope", function( $scope ) {
                $scope.bananas_eaten = 0;
                $scope.cooling_down = false;
                $scope.eat_banana = function() {
                    $scope.bananas_eaten++;
                    $scope.cooling_down = true;
                    window.setTimeout( function() {
                        console.log( "setTimeout callback fired" );
                        $scope.cooling_down = false;
                        console.log( "Current value of cooling_down: " + $scope.cooling_down );
                    }, 1000 );
                };
                $scope.can_eat_banana = function() {
                    return !$scope.cooling_down;
                };
            } ] );
        }() );
    </script>
</body>
</html>

Angular 在window.setTimeout()后不会再次运行其摘要循环(实际上,它甚至没有收到通知)。

请改用$timeout()

更新模型(范围的属性)时,必须进行摘要才能使该更改影响 UI。当 window.setTimeout 完成时,原始摘要(由单击引起)早已完成,另一个摘要未启动,因此 UI 永远不会更新。

若要解决此问题,请将window.setTimeout替换为 $timeout 服务,该服务将在超时完成后启动另一个摘要。