Como acessar / atualizar $ rootScope de fora Angular

Meu aplicativo inicializa um grafo de objects em $ rootScope, assim …

var myApp = angular.module('myApp', []); myApp.run(function ($rootScope) { $rootScope.myObject = { value: 1 }; }); 

… e, em seguida, consome dados desse gráfico de object (somente binding unidirecional), assim …

 

The value is: {{myObject.value}}

Isso funciona bem, mas se eu subsequentemente (após a renderização da página ter sido concluída) tentar atualizar o $ rootScope e replace o object original por um novo, ele será ignorado. Inicialmente presumi que isso acontecia porque o AngularJS mantém uma referência ao object original, mesmo que eu o tenha substituído.

No entanto, se eu envolver o HTML de consumo em um controlador, poderei atualizar repetidamente seu escopo da maneira pretendida e as modificações serão refletidas corretamente na página.

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

Existe alguma maneira de conseguir isso através do $ rootScope, ou só pode ser feito dentro de um controlador? Além disso, existe um padrão mais recomendado para implementar essas operações? Especificamente, eu preciso de uma maneira de replace os charts de objects completos que são consumidos pelo AngularJS de fora do código do AngularJS.

Obrigado, antecipadamente, por suas sugestões, Tim

Edit: Como sugerido nos comentários, tentei executar a alteração dentro de $ apply, mas isso não ajuda:

 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); 

Exceto para casos muito raros ou para fins de debugging, fazer isso é apenas uma prática BAD (ou uma indicação de design de aplicativo BAD)!

Para casos muito raros (ou debugging), você pode fazer assim:

  1. Acesse um elemento que você conhece como parte do aplicativo e envolva-o como um elemento jqLite / jQuery.
  2. Obtenha o escopo do elemento e, em seguida, o $rootScope acessando .scope().$root . (Existem outras maneiras também.)
  3. Faça o que você fizer, mas envolva-o em $rootScope.$apply() , então Angular saberá que algo está acontecendo e fará sua mágica.

Por exemplo:

 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 ! :('; }); } 

Veja também esta pequena demonstração .


EDITAR

O Angular 1.3.x introduziu uma opção para desabilitar o debug-info de ser anexado a elementos DOM (incluindo o scope ): $ compileProvider.debugInfoEnabled ()
É aconselhável desativar o debug-info na produção (por causa do desempenho), o que significa que o método acima não funcionaria mais.

Se você quiser apenas depurar uma instância ao vivo (produção), poderá chamar angular.reloadWithDebugInfo () , que recarregará a página com o debug-info ativado.

Alternativamente, você pode ir com o Plano B (acessando o $rootScope através do injetor de um elemento):

 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 ! :('; }); } 

Depois de atualizar o $ rootScope, chame $ rootScope, $ apply () para atualizar as ligações.

Pense em modificar os escopos como uma operação atômica e $ apply () confirma essas mudanças.

Se você deseja atualizar o object do escopo da raiz, injete $rootScope no seu controlador:

 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); }); 

Violino demo