62
votes

I have a question similar to this one, but different.

Here I am trying to add an event listener for a window.postMessage handler.

app.run(function ($location, $window, $rootScope) {
  $window.addEventListener('message', function(e) {
      $location.path("/abc");
      console.log($location.path()); // this prints "/abc" as expected

      $rootScope.$apply(); // this has no effect

      $scope = angular.element(document).scope(); // this is the same as $rootScope
      $scope.$apply(); // so this also has no effect
  });
});

The $location.path isn't being recognised by Angular.

The other question says that I should call $apply() on the scope, but the only scope available to me is $rootScope and calling $apply() on that doesn't seem to work.

A comment on the answer suggests that a scope can be got with

$scope = angular.element(document).scope()

but this gives me the $rootScope, which doesn't work.

How do I get angular to regocnise the change in $location.path()? Is there a better way to register a message callback in such a way as I can change the path?

4
and you see that event fires properly? - Ivan Chernykh
If you mean does the event listener fire then yes, I have a debugger breakpoint. - Joe

4 Answers

118
votes

You should run the expression as function in the $apply() method like

app.run(function ($location, $window, $rootScope) {
  $window.addEventListener('message', function(e) {
      $rootScope.$apply(function() {
        $location.path("/abc");
        console.log($location.path());
      });
  });
});

See documentation - ng.$rootScope.Scope.

If you want to improve testability, use $console instead of console and inject that object as well.

10
votes

The accepted solution is not the appropriate way to do it. Ideally you shouldn't need to interfere with the digest cycle (which $apply() does).

The best way according to me is calling $location.path() from the MainController. Use $emit - $on to send it to the MainController and have a function call $location.path from there. It will redirect you flawlessly without having to interfere with the digest cycle. I hope this helps someone.

1
votes

I had the same problem, my mistake was that I was running the ng-click directive badly on my item.

Bad example:

<a **href="#"** ng-click="myFunctionChangeLocationPath()">...

Example well:

<a ng-click="myFunctionChangeLocationPath()">...

And so you can correct the problem!

0
votes

There is solution without $timeout.

Just prevent redirect to default state before using $state.go

event.preventDefault();
$state.go('root');