问题描述
我试图了解如何通过控制器正确地操纵属性。 以下代码在四秒钟内执行六个更新。 更新二和三未反映在视图中。 为什么会这样,要使这些类型的更新影响视图,我该怎么办?
HTML
<div ng-controller="Controller">
myValue: <span ng-bind="myValue"></span>
</div>
使用Javascript
var app = angular.module('myApp', []);
app.controller('Controller', function ($scope, $interval) {
$scope.myValue = "first";
console.log($scope.myValue);
setTimeout(function() {
$scope.myValue = "second"; // never updates
console.log($scope.myValue);
$scope.$emit("my-event", "third"); // never updates
console.log($scope.myValue);
$interval(function() {
$scope.$emit('my-event', "fourth");
}, 1000, 1);
}, 1000);
$interval(function() {
$scope.myValue = "fifth";
console.log($scope.myValue);
$interval(function() {
$scope.$emit("my-event", "sixth");
}, 1000, 1);
}, 3000, 1);
$scope.$on('my-event', function (event, arg) {
$scope.myValue = arg;
console.log(arg);
});
});
1楼
使用$timeout
而不是setTimeout
选择加入摘要周期。
second
不会显示由于消化周期交替覆盖的值myValue
。
更新的小提琴: :
2楼
您可以尝试使用{{myValue}}
代替<span>
元素
3楼
因此,我显然对原始问题不够清楚,因为(正确地)建议的答案建议使用$timeout
而不是setTimeout
,但是原始意图是要理解为什么更新未反映在视图中,以及可能是什么使这些类型的更新( 起源于angular之外 )会按预期影响视图。
阅读范围指南
因此,尽管我选择跳过开发人员指南的“ ,因为它看起来最无聊,但它可能是最重要的,它清楚地指出了理解角度绑定数据的方式必不可少的一些项目,尤其是作用域生命周期笔记;
当浏览器调用JavaScript时,代码将在Angular执行上下文之外执行,这意味着Angular不了解模型修改。 为了正确地处理模型修改,执行必须使用
$apply
方法进入Angular执行上下文。 Angular将只考虑在$apply
方法内部执行的模型修改。
这里有一个很好的可以进一步解释这个概念。 第一种说法恰当地重申了理解范围的重要性:
您需要了解Angular的工作方式才能理解它。
不要只调用$ scope。$ apply
因此,您开始将调用添加到$scope.$apply
周围的地方,以解决源自angular之外的这些问题,但最终您开始得到:
Error: $digest already in progress
这意味着您不能在$digest
执行期间调用$scope.$apply
。
之后,您可能会想,如何根据$digest
当前是否在运行,有条件地调用$scope.$apply
。
但是,您不需要这样做...
只需使用$ timeout
哈,我知道像被一样,但是我认为是基于不同的思维过程。
看到 。
$timeout
不仅用于代替setTimeout
,而且还用于(无delay
)包装从Scope生命周期外部调用的任何模型更新,这样做可以确保与当前正在处理的$digest
不冲突。
包起来
因此,在原始代码段中,第二和第三次更新未在视图中反映出来,因为它们是在Angular执行上下文之外执行的。 第三次更新不会影响模型这一事实也意味着在执行上下文之外调用事件也不会使您进入执行上下文。
第四个更新已经包装在$interval
,它本身使更新在下一个摘要中运行。
因此,更新代码以显示角度执行上下文之外的事件示例,该事件导致其更新显示在视图中,如下所示:
setTimeout(function() {
$timeout(function({ // start wrap
$scope.myValue = "second"; // now this updates!
console.log($scope.myValue);
$scope.$emit("my-event", "third"); // now this updates!
})); // end wrap
console.log($scope.myValue);
$interval(function() {
$scope.$emit('my-event', "fourth");
}, 1000, 1);
}, 1000);