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.
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
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
I'd like to be able to update a scope in Angular from a function outside Angular.
E.g., if I have a jQuery plugin which returns a success callback, I'd like to be able to update the scope from that success callback. Every solution I've seen for this involves calling angular.element(selector).scope and then calling $apply on the scope that is returned. However, I've also seen many comments indicating that this doesn't work when debug info is off and thus it isn't recommended, but I haven't seen any alternative solutions.
Does anyone know of a way to update the scope from outside of Angular without using angular.element(selector).scope?
Here is the accepted solution in the post:
"You need to use $scope.$apply() if you want to make any changes to a scope value from outside the control of AngularJs like a jQuery/javascript event handler.
function change() {
alert("a");
var scope = angular.element($("#outer")).scope();
scope.$apply(function(){
scope.msg = 'Superhero';
});
}
Here is a warning that .scope() doesn't work when debug data is off in the post:
"FYI according to the docs using .scope() requires the Debug Data to be enabled but using Debug Data in production is not recommended for speed reasons. The solutions below seem to revolve around scope() – rtpHarry Dec 5 '14 at 15:12 "
I don't see any alternative solution to using .scope() in this post or in other similar posts.
AngularJS access scope from outside js function
Thanks!
Update
One possible solution to not using angular.element(selector).scope was I assigned the scope in the controller I was using FirstCtrl to the window object. I injected $window into the FirstCtrl controller and did the following:
$window.FirstCtrlScope = $scope;
Then from jQuery or other javascript I could do:
var scope=window.FirstCtrlScope;
scope.$apply(function () {
// update the scope
});
Is this a good solution or are there better solutions for updating a scope without using angular.element(selector).scope?
Thanks!
I think both ways are bad. Design which sets controllers as global variables, or access to scope by html element leads to unmaintainable application with many hidden links.
If you need cooperate with jQuery plugins (or other non-angular code), wrap it into directive with clear API (attributes, bindings and callbacks).
You can assign the scope to a data attribute on the element, then access that. Take a look here where the angular-ui-bootstrap library implemented that approach.
Based on this Plunker: http://plnkr.co/edit/GufJjrn3OxYVSf2oLD5n?p=preview
I have two directives, for the sake of simplicity, let's name them directiveBlue and directiveRed.
directiveRed has to be a sub element of directiveBlue.
The MainCtrl of our mini app has a simple array under the variable $scope.elements.
This variable is passed to the isolate scope that directiveBlue creates via the data-elements attribute. Notice that the directiveBlue has to be a transclude directive.
Then my main problem is, how do I pass the array of elements to the directiveRed without having to get it doing it via $scope.$parent.elements which seems to me, is a bad practice and then it makes the code tightly coupled.
Any changes to the elements in the deepest directive should then be synched with the rest of the scopes.
Is there any good practice or valid solution for this?
Thanks!
EDIT:
To be more concrete on my use case:
I've created a plunker (http://plnkr.co/edit/i2Busz6E8ehlkG3uEllh?p=preview) with a more concrete situation, where I want to have directive for an action group, I've implemented an option as a simple directive and I want to place my logic in the directives controllers. The method selectAll is pretty simple, but I can imagine having more complex actions which would require the elements from the top scope.
there are plenty of solutions, but without knowing what your goal is, it is more or less guessing.
The following quote is from the angular docs for $compile and describes the use of controller
(...)The controller is instantiated before the pre-linking phase and it is shared with other directives (see require attribute). This allows the directives to communicate with each other and augment each other's behavior. The controller is injectable(...)
a fork of your plnkr to show how to access MainCtrl's $scope.elements in directiveBlue and directiveRed
I have a question that came up in a meeting that I would like to get some expert opinion on.
lets say I have a 'userService' that many controllers in my app need. (in fact most of them).
It was my belief that any parent controllers would inject this userService and expose properties of that service to child controllers. Since any 'child' controllers have access to parent scope, there is no need to inject the 'userService' into every controller.
Another person's opinion was to inject the 'userService' into every controller that needs it, regardless if the service is already inherited from it's parent, basically because it makes the code easier to read and easy to find where your variables are coming from.
To me, this view seems wrong, and misses the point of Angular and how it uses prototype inheritance, and the power of scope. Doesn't this actually create unnecessary instances in memory that reference the same thing ? why would you want to do that ?
I would love to hear some experience opinions on this.
Thanks!
The references in memory are cheap. Don't worry about those. More importantly, you're implicitly creating a dependency that leads to a non-reusable component when you put something on scope that a child needs to do its' job. You should instead declare your dependencies upfront--via the argument list. You'll thank yourself later when you come to testing. Scope inheritance is for templates--not controllers.
There's some arguments both ways, but I avoid scope inheritance whenever possible. I even isolate scopes on most of my directives. The reason is that you can more naturally de-couple and encapsulate your code. Encapsulation is less of a problem on controllers in general, but it's nice to know that wherever you are in a scope hierarchy you can always get to your service.
Someone can correct me if I'm wrong, but I think it works like this.
Controller A injects service A. It can access it via A.method
Controller B inherits from Controller A but cannot access service A as it doesn't know what it is.
Scope inheritance comes into play when you assign that service to a scope variable. If we change it now so that Controller A sets $scope.A = A THEN Controller B will be able to access the service via $scope.A.method as it has inherited this.
I don't think the injected services themselves are inherited.
In terms of memory, I wouldn't worry too much. Angular won't be creating a new instance, just a reference to it. Services are singletons and won't get recreated unless they're destroyed. If you're using Factories, then you may get new objects but that's different.