Domanda

In my AngularJS application, I have a Session service object that contains stuff like the current user, their preferences, the current company they belong to, and the current theme that they are using. Many places in my application refer to the Session service when they need to get at this data.

Because these variables are in a service, I cannot use scope watches to detect changes. So instead, I've decided to use the observer pattern. Various places in the application, such as other services, directives, controllers, etc. will register themselves with the Session service and provide a callback to be executed whenever the Session changes.

For example, if the user changes their theme, the <style> element in index.html that uses a custom directive will be notified, and it will recreate all of the overriding css rules for the new colors.

For another example, whenever the user's avatar is updated, the main menu bar controller will be notified to refresh and redraw the avatar. Stuff like this.

Obviously the data in Session has to be refreshed at least once before the various controllers, directives, etc. use it. The natural place to ask the Session service to get its session data was in a run block for the application-level module. This works pretty well, but I don't think it's the best place either.

One problem I have noticed is that when Firebug is open, the asynchronous nature of things loading causes ordering issues. For example, the directive that runs on the <style> element will run AFTER the Session service has refreshed in the application's run block... which means the theme will not get updated after pressing F5 because the callback is registered after the initialization of the data occured. I would have to call a manual refresh here to keep it in sync, but if I did that, it may execute twice in the times where the order is different! This is a big problem. I don't think this issue is just related to Firebug... it could happen under any circumstance, but Firebug seems to cause it somewhat consistently, and this is bad.

To recap... This asynchronous ordering is good:

  1. Theme Directive registers callback to Session
  2. Menu Bar application controller registers callback to Session
  3. Session.refresh() is called in .run block.

This asynchronous ordering is bad:

  1. Menu Bar application controller registers callback to Session
  2. Session.refresh() is called in .run block.
  3. Theme Directive registers callback to Session, but callback does not get executed since Session.refresh() was already executed.

So rather than use the observer pattern, or refresh the Session state via a run block, what the best way to design the services, etc. so that the session data will ALWAYS get refreshed after (or maybe before) the various other parts of the application require it? Is there some kind of event I can hook into that gets executed before directives and controllers are executed instead of the run block?

If my approach is generally sound, what can I add to it to really make it work the way it should?

Thanks!

È stato utile?

Soluzione

In angular.js you have 2 way of using global variables:

  1. use a $rootScope
  2. use a service

Using $rootScope is very easy as you can simply inject it into any controller and change values in this scope. All global variables have problems! Services is a singletons(What you need)!

I think in your case you can use

$rootScope

And

$scope.$watch

Great answer

Altri suggerimenti

Is there a reason you can't access the variables directly like this:

app.factory('SessionService', function() {
    var items = {
        "avatar": "some url"
    };
    return items;
});

var MainController = [$scope, 'SessionService', function($scope, SessionService){
    $scope.session = SessionService;

    $scope.modifyAvatar = function(url){
        $scope.session.avatar = "some new url";
    };
}];

var HeaderController = [$scope, 'SessionService', function($scope, SessionService){
    $scope.session = SessionService;

    // You probably wouldn't do this, you would just bind
    // to {{session.avatar}} in your template
    $scope.getAvatar = function(){
        return $scope.session.avatar;
    };
}];
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top