Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 711
AngularChef Avatar asked Aug 10 '12 18:08

AngularChef


People also ask

How to use location path in AngularJS?

The $location in AngularJS basically uses a window. location service. The $location is used to read or change the URL in the browser and it is used to reflect that URL on our page. Any change made in the URL is stored in the $location service in AngularJS.

What is rootScope broadcast in AngularJS?

$broadcast in AngularJS? The $rootScope. $broadcast is used to broadcast a “global” event that can be caught by any listener of that particular scope. The descendant scopes can catch and handle this event by using $scope.

What is the difference between emit and broadcast in AngularJS?

The difference between $broadcast() and $emit() is that the $broadcast() sends the event from the current controller to all of its child controllers. The $emit() method sends an event from current controller to all of its parent controllers.

What event is broadcast if the same instance of a route is being reused?

$routeUpdate. Broadcasted if the same instance of a route (including template, controller instance, resolved dependencies, etc.) is being reused. This can happen if either reloadOnSearch or reloadOnUrl has been set to false .


1 Answers

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

like image 183
mna Avatar answered Sep 20 '22 02:09

mna