Why using $rootScope with functions is not recommended?
Asked Answered
T

3

13

While am looking into the FEQs of Angularjs I've seen below article:

$rootScope exists, but it can be used for evil

Scopes in Angular form a hierarchy, prototypally inheriting from a root scope at the top of the tree. Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.

Occasionally there are pieces of data that you want to make global to the whole app. For these, you can inject $rootScope and set values on it like any other scope. Since the scopes inherit from the root scope, these values will be available to the expressions attached to directives like ng-show just like values on your local $scope.

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.

Conversely, don't create a service whose only purpose in life is to store and return bits of data.

— AngularJS FAQ - $rootScope exists, but it can be used for evil

So My doubt is why $rootScope is not recommended for functions as a global function? Is there any performance issue?

Trackless answered 24/9, 2015 at 12:37 Comment(2)
just polluting an object that gets copied to create new scopes and those scopes in turn get copied into child scopes that can be nested many levelsSchmo
This is not true. Since the scope inheritance in angular is implemented as plain javascript prototypical inheritance, nothing ever will get copied. If a member $scope.foo is not found in the current scope, the interpreter will continue to search the member in the object pointed to by $scope._proto_ (which happens to be the parent scope, or if there is no parent, the rootScope), then this objects _proto_, etc. Since this is a very common thing in javascript all engines heavily optimize for it and even long prototype chains wont significantly slow down your code.Sustentacular
T
11

I've answered this in the past, but it's good that you're asking these questions.

$rootScope exists, but it can be used for evil Scopes in Angular form a hierarchy, prototypally inheriting from a root scope at the top of the tree. Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.

Non-isolated scopes are hierarchical, but most developers should be using directives that have isolated scopes. The very hierarchical nature of AngularJS's scope is the source of many bugs in angular apps. It's a problem I like to call scope bleeding where a scope property is modified magically somewhere in the DOM tree and you don't know why.

Angular's default behavior is to inherent scopes and this makes it tempting for one controller to update something managed by another controller, so on, and so on. This is how spaghetti connections between source code is created. Making it very difficult to maintain that code.

Occasionally there are pieces of data that you want to make global to the whole app. For these, you can inject $rootScope and set values on it like any other scope.

No that's not correct. AngularJS allows you to define things like constants, values, and services. These are things that can be injected into routes, controllers and directives. That is how you make things accessible globally to your app, and this how you do it if you want to make your controllers or directives testable. A unit test writer doesn't know what properties should be in the $rootScope that a directive or controller depends upon. They have to assume that the $rootScope has not mutated to provide a service or data.

Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language.

The problem isn't $rootScope but what people are doing with it. Many apps add the current user, the auth tokens, and the session data into the rootScope. This ends up getting used heavily in templates (shows X if user logged in otherwise show Y). The problem is that the HTML doesn't communicate scope hierarchy. So when you see {{user.firstname + ' ' + user.lastname}} you have no idea where the variable user came from. The second problem is child scopes can shadow root properties. As in the previous example if a directive does this scope.user = 'bla bla bla'. It hasn't replaced the value on the rootScope. It's hidden it. Now you get some strange unexpected things in the templates, and you don't know why the variable user has changed.

Conversely, don't create a service whose only purpose in life is to store and return bits of data.

Angular's $cacheFactory and $templateCache are examples of services that exist only too store data. I think the author was trying to encourage the use of constants and values in Angular's modules, but that's not a good description to do that.

So My doubt is why $rootScope is not recommended for functions as a global function? Is there any performance issue?

The $rootScope is the only scope available during angular.config(..). It's during this time that the scope can be modified if this is the only time that you can do it. For example; you may need to inject an API key or Google anayltics variable before the app starts.

Functions on any scope are generally a bad idea. Mainly for the reason that everything in scopes is digested in expressions on the templates. Functions tent to hide heavy operations. It's impossible to tell how heavy a template is by reading the HTML when it calls a function. I've seen scope functions like getHeight() where the function itself performed 3 levels of nested loops. That function has to get called every time angular digests the watchers to see if it's changed. You should try to keep your templates as dry as possible.

Ticktock answered 24/9, 2015 at 14:8 Comment(3)
I think I know where the author was going with the services that only store and return bits of data. I recently advised a team to get rid of their variables on $rootScope. There are better ways of accomplishing the functionality they wanted. They resolved the issue by moving them into a service with getters and setters. Clearly they've just moved their global state issue.Starlin
"The $rootScope is the only scope available during angular.config()'" ? If I'm not wrong I can not access to $rootScope on configuration phase, I'm pretty sure that this config bloque its just for providers. Did you men .run() bloque? please correct me if I'm wrong.Epineurium
@razorblade yes, you are correct. There is a root scope provider, but that just lets you adjust the digest limits.Ticktock
H
4

Global Variables are Abused

$rootScope is pretty much a global variable and has its place but is definitely abused by most of the people that use it. These are the reasons Globals in general should not be used.

Non-locality -- Source code is easiest to understand when the scope of its individual elements are limited. Global variables can be read or modified by any part of the program, making it difficult to remember or reason about every possible use.

No Access Control or Constraint Checking -- A global variable can be get or set by any part of the program, and any rules regarding its use can be easily broken or forgotten. (In other words, get/set accessors are generally preferable over direct data access, and this is even more so for global data.) By extension, the lack of access control greatly hinders achieving security in situations where you may wish to run untrusted code (such as working with 3rd party plugins).

Implicit coupling -- A program with many global variables often has tight couplings between some of those variables, and couplings between variables and functions. Grouping coupled items into cohesive units usually leads to better programs.

Concurrency issues -- if globals can be accessed by multiple threads of execution, synchronization is necessary (and too-often neglected). When dynamically linking modules with globals, the composed system might not be thread-safe even if the two independent modules tested in dozens of different contexts were safe.

Namespace pollution -- Global names are available everywhere. You may unknowingly end up using a global when you think you are using a local (by misspelling or forgetting to declare the local) or vice versa. Also, if you ever have to link together modules that have the same global variable names, if you are lucky, you will get linking errors. If you are unlucky, the linker will simply treat all uses of the same name as the same object.

Memory allocation issues -- Some environments have memory allocation schemes that make allocation of globals tricky. This is especially true in languages where "constructors" have side-effects other than allocation (because, in that case, you can express unsafe situations where two globals mutually depend on one another). Also, when dynamically linking modules, it can be unclear whether different libraries have their own instances of globals or whether the globals are shared.

Testing and Confinement - source that utilizes globals is somewhat more difficult to test because one cannot readily set up a 'clean' environment between runs. More generally, source that utilizes global services of any sort (e.g. reading and writing files or databases) that aren't explicitly provided to that source is difficult to test for the same reason. For communicating systems, the ability to test system invariants may require running more than one 'copy' of a system simultaneously, which is greatly hindered by any use of shared services - including global memory - that are not provided for sharing as part of the test.

Source: http://c2.com/cgi/wiki?GlobalVariablesAreBad

Sharing data in Angular

When it comes to sharing data across controllers in Angular you should use a service. With your custom service you can create a getter and a setter method. You inject it to the controllers you need it and can use it in your app.

Hydrant answered 24/9, 2015 at 12:55 Comment(2)
If you quote a whole section you should consider making a reference to the original content. c2.com/cgi/wiki?GlobalVariablesAreBadSustentacular
sorry I forgot, I'll add it nowHydrant
D
1

There exist no performance issues. It would actually boost your performance by a fraction of time, because you dont need to dependency-inject a lot of services.

But it's a big concern of design. Consider a large application with dozens and dozens of views, complex components and tied to a number of well known APIs (e.g. Twitter, Flickr, Facebook, OAuth, ...).

You wont develop this application alone. The following issues will arise:

Namespacing

You are working on the Facebook API, someone else is working on the Twitter API. You both think that using $rootScope for functions is a good idea and both write a $rootScope.login function. How would you resolve this when doing git merge? You need namespacing, alas, you need to develop two services myFacebookAPI, myTwitterAPI which then can implement the same interface for loggin in. (login(user,pw)). Note that this gives you the ability to abstract away the actual social network you are dealing with in the controller, when you can do something like:

$scope.callAction = function (action) {
var service;
    if ($scope.serviceSelected === 'fb') {
         service = myFacebookAPI;
    } else {
         service = myTwitterAPI;
    }
    service[action]();
};

Testing

When developing professionally, you write tests. Angular gives you tools to do automated tests for services, etc., but you wont be able to test something you assign to $rootScope in the same comfortable manner.

Other issues will arise too, but i think this should be enough for you to think about it on your own.

Divulgate answered 24/9, 2015 at 13:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.