2

As the Angular team (and severals blogs) adviced, I try to avoid using $rootScope as possible in my apps.

$rootScope exists, but it can be used for evil

"Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language. In particular, don't use it for code, only data. If you're tempted to put a function on $rootScope, it's almost always better to put it in a service that can be injected where it's needed, and more easily tested." - from Angular FAQ

But in one of my apps, I use several times events such as $routeChangeStart that are broadcast events using $rootScope:

/* Force redirection to home page when a connected user calls login page */
$rootScope.$on("$routeChangeStart", function(event, next, current) {
    if(next.$$route.originalPath == '/login' && Session.isUserConnected()) {
        $location.path("/home");
    }
}

I don't see a way to avoid $rootScope in these cases.

  • Is there a more elegant way to do this kind of tricks?
  • Is that a design error of my app to use it?
  • Must I resign myself to use it when absolutely necessary?

Tell me if you want me to provide more code samples.

Mistalis
  • 17,793
  • 13
  • 73
  • 97
  • Did you read this `global state sucks and you should use $rootScope sparingly`??? – Ramesh Rajendran Jan 25 '17 at 08:24
  • @RameshRajendran Yes, from the [first link](https://docs.angularjs.org/misc/faq) I provided (last paragraph). It is from the **Angular documentation FAQ**. – Mistalis Jan 25 '17 at 08:24
  • for this case you are just listening to an event broadcasted to $rootScope, I think this is a valid use case. – Icycool Jan 25 '17 at 08:40

2 Answers2

1

If you don't want to use $rootScope in this scenario yyou can achieve this this using the powerful resolve.

Since I have seen you are using the ngRoute module, I created the Plunker and code for ngRoute resolve.I can create one for ui-router if you want it also.

Using Resolve makes the code to execute faster since it will not undergo the DigestCycle

You can get the user flag from the Session.isUserConnected() by injecting the session.

Then you can use that variable to redirect the route.

// Code goes here

angular.module('Test', ['ngRoute'])
.config(function($routeProvider) {
     var user = true;
     //var user = Session.isUserConnected(); You can use your logic here
    $routeProvider
   .when("/login", {
        template: "Login View",
        resolve: {
           
         data: function($q, $timeout,$location) {
             var deferred = $q.defer();
            $timeout(function() {
              
              if (user) {
                $location.path("/home");
                deferred.reject();
              } else {
                deferred.resolve();
              }
            });
    
            return deferred.promise;
            }
        }
    })
     .when("/home", {
        template: "Home View. It is redirected from Login by checking the user condition. Without using the rootscope",
      })
    .otherwise({redirectTo:'/login'});
})

 
  
<!DOCTYPE html>
<html ng-app="Test">
  <head>
    <link rel="stylesheet" href="style.css" />
  </head>
 
  <body>
    <div ng-view></div>
    
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>
    <script src="http://angular-ui.github.io/ui-router/release/angular-ui-router.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.js"></script> 
  </body>
</html>

The main part goes here:

data: function($q, $timeout, $location) {
    var deferred = $q.defer();
    $timeout(function() { 
        if (user) {
            $location.path("/home");
            deferred.reject();
        } else {
            deferred.resolve();
        }
    });
    return deferred.promise;
}

HERE IS A WORKING DEMO

Mistalis
  • 17,793
  • 13
  • 73
  • 97
Sravan
  • 18,467
  • 3
  • 30
  • 54
  • Interesting point of view. In terms of performance and design, is that better than using `$rootScope`? Do you think I **should** use it, or is that only a suggestion of "how to do differently"? – Mistalis Jan 25 '17 at 10:39
  • Since a change in `$rootScope` will run the digest cycle once again and resolve runs only once while a route is called, resolve may be better when compared to `$routeScope`, also, as you asked it is an alternative to `$rootScope`. – Sravan Jan 25 '17 at 10:43
  • It does not *answer*, as I think it is still more complex and not sure better about performance. Anyway I upvoted your effort of showing another way to do this kind of things. :-) – Mistalis Jan 25 '17 at 13:57
  • Np, wait for few more answers and I will also search for otherways of doing it. :-) – Sravan Jan 25 '17 at 14:11
0

You missread that. First of all, this event was added by Angular team - do you think they recommend not to use it?:D That article is about that you should not put everything to $rootScope, which seems to be an easy way for beginners.

Actually, using $rootScope - for for global events is absolutely ok -- that is actual purpose of this service.

Petr Averyanov
  • 9,327
  • 3
  • 20
  • 38
  • I wonder if there are "good practices" about how to structure this code (as it in on $rootScope, you can write it everywhere) so that it remains as clear and as readable as possible – Mistalis Jan 26 '17 at 13:08
  • "global things that are always true", should be in app.run(). -- (cause it will always execute on your app init) – Petr Averyanov Jan 26 '17 at 13:30