【问题标题】:How to access/update $rootScope from outside Angular如何从 Angular 外部访问/更新 $rootScope
【发布时间】:2023-03-24 16:14:01
【问题描述】:

我的应用程序在 $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 代码外部使用的完整对象图。

提前感谢您的建议, 蒂姆

编辑:按照 cmets 中的建议,我尝试在 $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);

【问题讨论】:

  • 范围是如何更新的?如果它来自 Angular 之外的事件,您需要告诉 Angular 运行摘要
  • @charlietfl:我尝试修改 $apply() 中的根范围,但没有帮助。我更新了我的问题以反映我的尝试。
  • 建议您创建一个复制问题的演示。目前尚不完全清楚为什么您只处理 rootscope 而不能在您的应用中使用控制器

标签: angularjs rootscope


【解决方案1】:

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

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

  1. 访问您知道是应用程序一部分的元素并将其包装为 jqLit​​e/jQuery 元素。
  2. 通过访问.scope().$root 获取元素的范围,然后是$rootScope。 (还有其他方法。)
  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 :(';
  });
}

另请参阅此short demo


编辑

Angular 1.3.x 引入了一个选项来禁用调试信息附加到 DOM 元素(包括 scope):$compileProvider.debugInfoEnabled()
建议在生产中禁用 debug-info(出于性能考虑),这意味着上述方法将不再有效。

如果您只想调试一个实时(生产)实例,您可以调用 angular.reloadWithDebugInfo(),这将重新加载页面启用调试信息。 p>

或者,您可以使用 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 :(';
  });
}

【讨论】:

  • 如果您能解释为什么这是一种不好的做法并展示正确的做法,那么这个答案对除了 OP 之外的其他人会更有用。 :)
  • @Aerovistae:我无法展示正确的做法,因为任何做法都没有正确。您只是不应该这样做(除非您正在调试某些东西)。正确的方法是在 Angular 内部做任何你需要做的事情(你可以“正确”访问$rootScope)。原因的范围从损害可测试性/可维护性/声明性到更容易出错/难以调试/不直观等。
  • @ExpertSystem:我正在编写一个浏览器扩展程序,将 vanilla javascript 代码注入页面,并且需要影响使用 angularJS 编写的站点。我认为他最好的方法是获取 rootscope 和 brodcast angular-event。通过这种方式,我不需要对站点一无所知,除了消息名称。感谢您解决问题,尽管在 Angular 应用程序中使用它是一种不好的做法
  • 由于 Angular 应用程序本身需要了解扩展,我相信应该有更好(更“Angular”)的方式来做到这一点(例如,在元素上有一个指令并在那个元素什么的)。但无论如何,将浏览器扩展程序中的代码注入页面可能是一种特殊情况,足以证明从外部访问$rootScope 是合理的:)
  • 这篇文章向我展示了如何在本地范围可用时获取$rootScope而不注入它,即$scope.$root
【解决方案2】:

更新 $rootScope 后调用 $rootScope.$apply() 来更新绑定。

将作用域修改为原子操作,$apply() 会提交这些更改。

【讨论】:

    【解决方案3】:

    如果您想更新根作用域的对象,请将$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);
    });
    

    Demo fiddle

    【讨论】:

    • 这不是我要求的。我问如何从 Angular 外部修改根范围。我的应用程序没有控制器,不幸的是,我使用的架构不允许我创建控制器。上面示例中的控制器只是为了证明可以替换完整的对象图,并且 Angular 将根据该更改进行操作。
    • 如果你想访问控制器之外的根范围,使用:var rs = angular.element($('body')).scope(); 然后使用rs.$apply(function(){rs.myObject={...}})
    • 是的,谢谢,但是 Angular 忽略了我的更改。我更新了我的问题以反映我的尝试。
    • 作为@ExpertSystem 提供的解决方案,以下是在没有控制器的情况下访问和更新rootscope 对象的方法:jsfiddle.net/SU3mw/1
    • 这不是他要求的......他需要在角度环境之外使用$rootScope。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-22
    • 2019-05-02
    • 1970-01-01
    • 2019-03-29
    • 2019-12-13
    • 2018-03-19
    • 1970-01-01
    相关资源
    最近更新 更多