Как получить доступ/обновить $rootScope извне Angular

Мое приложение инициализирует граф объекта в $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);

Ответ 1

За исключением очень, очень редких случаев или отладки, это просто практическая практика (или указание дизайна приложения BAD)!

Для очень, очень редких случаев (или отладки) вы можете сделать это следующим образом:

  • Доступ к элементу, который, как вам известно, является частью приложения, и его переносить как элемент jqLite/jQuery.
  • Получите элемент Scope, а затем $rootScope, обратившись к .scope().$root. (Есть и другие способы.)
  • Делайте все, что вы делаете, но оберните его в $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, dude ! :(';
  });
}

См. также эту короткую демонстрацию.


ИЗМЕНИТЬ

Angular 1.3.x представила возможность отключить отладочную информацию от присоединения к элементам DOM (включая scope): $compileProvider.debugInfoEnabled()
Целесообразно отключить отладочную информацию в процессе производства (для повышения производительности), что означает, что вышеуказанный метод больше не будет работать.

Если вы просто хотите отлаживать экземпляр live (production), вы можете вызвать angular.reloadWithDebugInfo(), который будет перезагрузите страницу с поддержкой .

В качестве альтернативы вы можете перейти к Plan 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, dude ! :(';
  });
}

Ответ 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);
});

Демо-скрипт