43

我的应用程序在 $rootScope 中初始化一个对象图,像这样......

var myApp = angular.module('myApp', []);

myApp.run(function ($rootScope) {
    $rootScope.myObject = { value: 1 };
});

...然后使用来自该对象图的数据(仅限单向绑定),就像这样...

<p>The value is: {{myObject.value}}</p>

这工作正常,但如果我随后(在页面渲染完成后)尝试更新 $rootScope 并用新对象替换原始对象,它将被忽略。我最初认为这是因为 AngularJS 保留了对原始对象的引用,即使我已经替换了它。

但是,如果我将消费 HTML 包装在控制器中,我就能够以预期的方式重复更新其范围,并且修改会正确反映在页面中。

myApp.controller('MyController', function ($scope, $timeout) {
    $scope.myObject = { value: 3 };

    $timeout(function() {
        $scope.myObject = { value: 4 };

        $timeout(function () {
            $scope.myObject = { value: 5 };
        }, 1000);
    }, 1000);
});

有什么方法可以通过 $rootScope 来完成,还是只能在控制器内部完成?此外,是否有更推荐的模式来实施此类操作?具体来说,我需要一种方法来替换 AngularJS 从 AngularJS 代码外部使用的完整对象图。

提前感谢您的建议,蒂姆

编辑:正如评论中所建议的,我尝试在 $apply 中执行更改,但它没有帮助:

setTimeout(function() {
    var injector = angular.injector(["ng", "myApp"]);
    var rootScope = injector.get("$rootScope");

    rootScope.$apply(function () {
        rootScope.myObject = { value: 6 };
    });

    console.log("rootScope updated");
}, 5000);
4

3 回答 3

94

除了非常非常罕见的情况或调试目的之外,这样做只是不好的做法(或表明应用程序设计不好)!

对于非常非常罕见的情况(或调试),您可以这样做:

  1. 访问您知道是应用程序一部分的元素并将其包装为 jqLit​​e/jQuery 元素。
  2. 获取元素的范围,然后$rootScope通过访问.scope().$root. (还有其他方法。)
  3. 做任何你做的事,但是把它包裹起来$rootScope.$apply(),这样 Angular 就会知道正在发生的事情并发挥它的魔力。

例如:

function badPractice() {
  var $body = angular.element(document.body);  // 1
  var $rootScope = $body.scope().$root;        // 2
  $rootScope.$apply(function () {              // 3
    $rootScope.someText = 'This is BAD practice :(';
  });
}

另请参阅这个简短的演示


编辑

Angular 1.3.x 引入了一个选项来禁用调试信息附加到 DOM 元素(包括scope):$compileProvider.debugInfoEnabled()
建议在生产中禁用调试信息(为了性能),这意味着上面方法将不再起作用。

如果您只想调试实时(生产)实例,您可以调用angular.reloadWithDebugInfo(),它将重新加载启用调试信息的页面

或者,您可以使用 B 计划($rootScope通过元素的注入器访问):

function badPracticePlanB() {
  var $body = angular.element(document.body);           // 1
  var $rootScope = $body.injector().get('$rootScope');  // 2b
  $rootScope.$apply(function () {                       // 3
    $rootScope.someText = 'This is BAD practice too :(';
  });
}
于 2014-07-06T13:20:29.197 回答
1

After you update the $rootScope call $rootScope.$apply() to update the bindings.

Think of modifying the scopes as an atomic operation and $apply() commits those changes.

于 2014-07-06T12:57:22.880 回答
-2

如果要更新根范围的对象,请注入$rootScope控制器:

myApp.controller('MyController', function ($scope, $timeout, $rootScope) {

    $rootScope.myObject = { value: 3 };

    $timeout(function() {

        $rootScope.myObject = { value: 4 };

        $timeout(function () {
            $rootScope.myObject = { value: 5 };
        }, 1000);

    }, 1000);
});

演示小提琴

于 2014-07-06T11:48:34.580 回答