I am new to angularjs. I have designed an angularJS service, which is as per the diagram given below :-
Global Service is used as a mean for inter-controller communication. That's, it contains data shared between parent and child controller. Grand Parent controller opens a popup dialog which has parentController2, which in turn open another popup, which has childController3.
Now, What I want is, data stored in global service must be set to null, when its associated controller is destroyed. It's because services are singleton in angularjs, as per my knowledge. So, I don't want service to hold variable throughout the life-cycle of application, even if they are not required.
Moreover, I am using controllerAs syntax and don't want to use $scope(I know that I can do garbage collection in '$destroy' event on scope), to make my stuff compatible with angularjs 2.0.
Is there any way, I can garbage collect variable in service, which are not required, from controller, when controllerAs syntax is used ?
Apologizing for newbie question. Thanks in advance.
using ng-view is one more solution over here for garbage collection as it destroy's the scope as soon as view is changed. It will be used with ngroute but still provides better mechanism for garbage collection.
You can check the ng-view code
function destroyLastScope() {
if (lastScope) {
lastScope.$destroy();
lastScope = null;
}
}
This function is called inside the directive that destroys the scope of the previous scope i.e if you go from parent to child the parent scope will be destroyed
Related
In angularjs, I see a lot of usages for $scope and $rootscope.
I like to understand the life term of variables attached to $scope and $rootscope.
Say, I have a.js/a.html and e.js/e.html.
a.js has $scope attached with variables say $scope.b, $scope.c, $scope.d for use in a.html.
If I load another page with e.js and e.html. Those $scope.b, $scope.c, $scope.d still have lives or they are removed. If I have $scope.b, $scope.c, $scope.d again in e.js, what could be issue?
I just want to highlight the life term of those attached variables.
$rootScope - Usually configured in your main app.js file where you define your angular main module and main configuration. Even if not configured, it can always be injected.
$scope - your current scope in the current controller (page).
$rootScope variables live as long as you dont refresh your page. That means that they exist trough all transitions of your SPA.
$scope variables live only on the current scope (again, as long as you dont refresh the page) and they will get disposed once you transition to a different controller/page.
$rootScope lives as long as the whole page does
$scope instances are destroyed whenever the controller or directive using it are no longer active
No javascript is persistent between full page loads in browser
$rootScope is global while $scope is local. When Controller is assigned to a page, so a $scope variable can be use here because it binds to this controller. But when we want to share its value across to other controllers or services, then $rootScope is being used (**there are alternative ways share values across but in this case we want to use $rootScope).
AngularJS Scope Life-Cycle
A property assigned with $scope cannot be used outside the controller in which it is defined whereas a property assigned with $rootScope can be used anywhere.
Which means when you want variable witch can be use in single variable to use in multiple controller (whole application), you can use $rootscope
$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 Docs Miscellaneous - FAQ
Say I have two views that share a controller. Both views use the ng-route service. If a scope variable is changed in one view and then you switch to the second view, how come the second view doesn't update?
Example: My controller has a variable that = "hello". I output this variable just fine in both views. In view one I have an onclick that updates the variable to say "Whatsup". That works fine, but after the event I switch views and the second view is outputting "hello" still. Is there a way to share these? If not, then what is a technique to share data?
I'd like you to know that AngularJS is designed for SINGLE PAGE APPLICATIONS (SPA) so this behavior is not supported by the framework.
Each view have a different scope even though they share the same controller. So updating a scope variable for one view won't update the other.
If you want this behavior, I suggest using $on to listen to events.
Also, on your code you have $scope.test = 'hi'; that is hard-coded on your controller with no conditions or whatsoever which is why it reflects on both of your views who share the same controller.
Best practice is to have one view per controller. As your site scales, it would get very hard to manage multiple templates/views with one controller.
A service is the way to share data between controllers.
For your example, you could have a activate functions that are called when each view is rendered that gets the updated variable from the service you inject. Your on lick function should save the variable to the service to make it available across controllers.
Can anyone explain the difference between $scope and $rootScope?
I think
$scope:
We can get ng-model properties in particular controller from the particular page by using this.
$rootScope
We can get all ng-model properties in any controller from any page by using this.
Is this correct? Or anything else?
"$rootScope” is a parent object of all “$scope” angular objects created in a web page.
$scope is created with ng-controller while $rootscope is created with ng-app.
The main difference is the availability of the property assigned with the object. A property assigned with $scope cannot be used outside the controller in which it is defined whereas a property assigned with $rootScope can be used anywhere.
Example: If in the example below you replace $rootScope with $scope the department property will not be populated from the first controller in the second one
angular.module('example', [])
.controller('GreetController', ['$scope', '$rootScope',
function($scope, $rootScope) {
$scope.name = 'World';
$rootScope.department = 'Angular';
}
])
.controller('ListController', ['$scope',
function($scope) {
$scope.names = ['Igor', 'Misko', 'Vojta'];
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="example">
<div class="show-scope-demo">
<div ng-controller="GreetController">
Hello {{name}}!
</div>
<div ng-controller="ListController">
<ol>
<li ng-repeat="name in names">{{name}} from {{department}}</li>
</ol>
</div>
</div>
</body>
According to Angular's Developer's Guide to Scopes:
Each Angular application has exactly one root scope, but may have several child scopes. The application can have multiple scopes, because some directives create new child scopes (refer to directive documentation to see which directives create new scopes). When new scopes are created, they are added as children of their parent scope. This creates a tree structure which parallels the DOM where they're attached.
Both controllers and directives have reference to the scope, but not to each other. This arrangement isolates the controller from the directive as well as from DOM. This is an important point since it makes the controllers view agnostic, which greatly improves the testing story of the applications.
$rootScope is available globally, no matter what controller you are in, whereas $scope is only available to the current controller and it's children.
In other way we can look at this; $rootScope is global while $scope is local. When Controller is assigned to a page, so a $scope variable can be use here because it binds to this controller. But when we want to share its value across to other controllers or services, then $rootScope is being used (**there are alternative ways, we can share values across but in this case we want to use $rootScope).
Your second question about how you define those two words are correct.
Lastly a bit off track, please use $rootScope with care. Similar to the way you use global variables, can be a pain to debug and you may accidentally change the global variable somewhere inside a timer or something which makes your reading incorrect.
Every application has atleast one single rootScope and its lifecycle is the same as the app and every controller can have it's own scope, that is not shared with others.
Have a look at this article :
https://github.com/angular/angular.js/wiki/Understanding-Scopes
I recommend you read the official in-depth Angular documentation for scopes. Start at the section 'Scope Hierarchies':
https://docs.angularjs.org/guide/scope
Essentially, $rootScope and $scope both identify specific parts of the DOM within which
Angular operations are carried out
variables declared as part of either the $rootScope or $scope are available
Anything that belongs to the $rootScope is available globally across your Angular app, whereas anything that belongs to a $scope is available within the part of the DOM to which that scope applies.
The $rootScope is applied to the DOM element that is the root element for the Angular app (hence the name $rootScope). When you add the ng-app directive to an element of the DOM, this becomes the root element of the DOM within which $rootScope is available. In other words, properties etc of $rootScope will be available throughout your entire Angular application.
An Angular $scope (and all of it's variables and operations) is available to a particular subset of the DOM within your application. Specifically, the $scope for any particular controller is available to the part of the DOM to which that particular controller has been applied (using the ng-controller directive). Note though that certain directives e.g. ng-repeat, when applied within a part of the DOM where the controller has been applied, can create child scopes of the main scope - within the same controller - a controller doesn't necessarily contain only one scope.
If you look at the generated HTML when you run your Angular app, you can easily see which DOM elements 'contain' a scope, as Angular adds the class ng-scope on any element to which a scope has been applied (including the root element of the app, which has the $rootScope).
By the way, the '$' sign at the start of $scope and $rootScope is simply an identifier in Angular for stuff that's reserved by Angular.
Note that using $rootScope for sharing variables etc. between modules and controllers isn't generally considered best practice. JavaScript developers talk about avoiding 'pollution' of the global scope by sharing variables there, since there may be clashes later on if a variable of the same name is used somewhere else, without the developer realising it's already declared on the $rootScope. The importance of this increases with the size of the application and the team that's developing it. Ideally the $rootScope will only contain constants or static variables, that are intended to be consistent at all times across the app. A better way of sharing stuff across modules may be to use services and factories, which is a another topic!
Both are Java script objects and the difference is illustrated by diagram as below.
NTB:
First angular application try to find the property of any model or function in $scope , if it doesn't
found the property in $scope , then it search in parent scope in upper hierarchy. If the property is
still not found in upper hierarchy then angular tries to resolve in $rootscope.
New styles, like John Papa's AngularJS Styleguide, are suggesting that we shouldn't be using $scope to save current page's properties at all. Instead we should use the controllerAs with vm approach where the view binds to the controller object itself. Then use a capture variable for this when using the controllerAs syntax. Choose a consistent variable name such as vm, which stands for ViewModel.
You will still need the $scope for its watching capabilities though.
when i tell my angularjs app to navigate backwards
$window.history.back();
i see that the previous view's controller is being reused. is there a way to force angularjs to instantiate a new controller?
i am using a ngView element on the page and populating it with templates. perhaps there is a way to dictate to the router that a new controller should be instantiated each time.
(why is stackoverflow insisting on more words before i can submit my questions?....)
I think you should be investigating the $templateCache. I believe in many cases, Angular does instantiate a "new" controller on this event, but your template cache injects the data/state of the view when you use the history service.
Really, it all depends on scope... if your forward progress (app interaction) keeps your state and view within or underneath (as a child of) the same scope, the controller will never be destroyed, however if you leave the scope, it should clean up. (this is actually javascript, and Angular wraps that behavior with $scope). The $templateCache either way will rebuild the state, including data models within your view's assigned controller.
You would have to go about this by intercepting the history event taken, search the $cacheFactory or $templateCache for the key/value pair matching the view in question, then run:
$templateCache.remove('keystring');
prior to allowing the history event to execute. via the $cacheFactory, you can use $templateCache.get() to return information about the views you have cached.
Check out:
- http://docs.angularjs.org/api/ng/service/$cacheFactory
My question is a lot like this: AngularJS seed: putting JavaScript into separate files (app.js, controllers.js, directives.js, filters.js, services.js) Just can I make a function in one of the files that can be called by another? And if so what do I have to add? The only reason I'm not adding to it is because it is closed. Any help would be great!
Accessing another controller would be completely wrong. You have a lot of ways to communicate between them. Here are 3 easy ones:
Scope: you can access the scope of all your parent controller using $parent or simply the $rootScope (don't overuse it or your root scope will be clutter). Remember that your scope always have your parent scope objects too until you change them. Using $watch on a scope variable can make you code cleaner when needed. (see the scope documentation)
Event: Pretty straightforward. You listen with $scope.$on and you send it with $scope.$broadcast or $scope.$emit (depending if you want the notice the children or the parents).
Service: if what you are trying to implement has no clear controller owner, you might have to consider using a service. Then, you can use it within all your controllers. (See Creating Services)
Most of the time, you will end up using the scope because you want to interact with your parent controller. Let me know if anything is unclear.