How to broadcast path changes to all views in an AngularJS app?

I have an AngularJS single-page app displaying 3 views (which are actually 3 directives). To illustrate my question, let's assume my UI is identical to that of GMail and that my 3 views are:

  • Navigation Pane (left) -- where GMail displays folders like "Inbox", "Drafts"...
  • Toolbar (top right) -- where GMail displays buttons
  • Content Pane (bottom right) -- where GMail displays messages

These 3 views need to update themselves whenever the path changes. For example:

  • The Navigation Pane needs to highlight a specific item.
  • The Toolbar needs to show/hide certain buttons.
  • The Content Pane needs to load and display specific data from the server.

What's the best way to do this in AngularJS?

So far, I have:

  1. Ruled out the use of $routeProvider.when() because I need to update three views, and $routeProvider only supports one ngView.
  2. Created a SERVICE that watches the current path and uses $rootScope.$broadcast() to warn the controllers that the path changed.
  3. (OR, IN LIEU OF #2) Created a CONTROLLER that does the same as #2.
  4. Caught the event broadcasted by #2 or #3 with $scope.$on() (I do this in a view controller).

This kind of works, but I have several issues:

  • The "path change" event is often broadcasted BEFORE my event listeners are in place, especially at initial page load. This is probably due to the fact that my view templates are loaded from the server and the listeners can't be set up before the templates have finished loading.
  • Isn't there a more efficient/automated way to watch for path changes in AngularJS? (See my code below, it seems pretty "manual".)
  • Does the code that watches for path changes belong in a SERVICE or in CONTROLLER? (I'd lean towards a service, since a controller is more to add behavior to a view.)
  • How to guarantee that my views will catch the "path changed" event? Should I use an event at all? (maybe I could store the path in a service, and have my views watch the service instead?)

My code to watch for path changes (I put this in a service or in a controller):

var watchExpression = function() { return $location.path(); };
var listener = function(newPath, oldPath) {
    // Broadcast event on $rootScope
    $rootScope.$broadcast('PathChanged', newPath);
};
$rootScope.$watch(watchExpression, listener);

Then, in a view controller, I catch the event:

$scope.$on('PathChanged', function(event, path) {
    // Update view based on new path.
});

Any help appreciated. Thanks.

Your final version seems fine with the locationChangeSuccess, but for others reading this, I think you ruled out the $routeProvider too quickly. You can have one ng-view for the main content pane that changes with the path, and then other independent ("static") controllers/templates for the navigation pane and toolbar.

Now to listen to route changes in these other 2 controllers:

$rootScope.$on('$routeChangeSuccess', function(evt, cur, prev) {
    ...do what you have to do here, maybe set a $rootScope.path as you did
})

All using native Angular functionality. I actually do this in http://provok.in, a website I built using Angular, and I have a similar layout (well, not exactly, but I have "static" sections and an ng-view for the main content, dynamically updated based on the path, by the routeProvider).