I have been playing with Angular for 1 month and every time I thought It would make sense to use emit and broadcast to communicate between different parts of the framework I found articles stating that we should avoid them if we can (for instance: http://seanhess.github.io/2013/10/14/angularjs-directive-design.html)
So far I focused on communication between directive and controller, and between controller and service:
Directive <--> Controller: To call controller's methods from directive I use isolated scope and callback. In the other direction I use state variables in the controller that are watched in the directive and when modified trigger directive's methods .
Controller <--> Service: same mechanism. I use service's methods directly in my controller (this is the classic usage of service) and state variables in the service that are watched in the controller.
My question is straightforward: could you give me examples and explanations when emit and broadcast are relevant ?
When you need to send a message or a request from inner scope to outer scope you can use $emit and when you want to do same from outer scope to inner scope we use $broadcast.
Related
I need some guidance to take the best practice for my task in AngularJS.
Task:
Inside the view : I have one parent controller and two child controllers.
Child controllers work with their own $scope and objects.
When I press save in the view, I need to get the data from child controllers to parent controller in order to prepare an object for posting it to the server.
I am getting confused of what is the best solution for this approach.
A common way of sharing data between controllers is to use use a service.
You could also broadcast updates to the parent controller
There are many different ways to achieve this..
Using $rootScope.
Using Services
Use broadcast and emit
Declare an object in parent controller and then modify the same object in the child controller.
Using $rootScope is not a good approach, as the $rootScope variable is destroyed when application is closed.
I will also not recommend broadCast or emit, until it's required.
Services, is good for communication b/w controllers, but again you have inject it and modify the methods.
In your, scenario, i would recommend to use the common $scope object variable, which is declared inside parent and used in child controllers, as all methods are inherited in the child controllers.
Above answers are useful, but below is an simple way to update a $scope object in parent controller from child scope as below.
In child controller, $scope.$parent.parentObject = updatedObject
Hope it helps!
There are three common ways to share data between controllers:
1 put data in service or factory. Since they are singleton, any controller injected with same service can share the data within that service
2 put data in $rootScope
3 use $broadcast or $emit to send events between scopes, and pass the shared data as event arguments
I have an HTML div , like
<div id="loader-container" data-ng-controller="LoaderController" ng-show="shouldShow">
Some html design
</div>
And in my controller
angular.module('core').controller('LoaderController', ['$scope','$location', 'Authentication', '$rootScope',
function($scope,$location, Authentication, $rootScope) {
$scope.shouldShow = true;
}
]);
And now, I want to hide that html div from another controller, That's why I tried to make $scope.shouldShow variable as false from another controller. So how can I make
$scope.shouldShow = false;
from another controller. Is it possible or any alternative way.?
every controller will create a new Scope.
Hence the changing the scope in one controller will not reflect in the scope of other controller.
If you want to share a property across the controllers create that variable on the $rootScope and use it.
In Controller
$scope.$root.shouldShow = true; //or false
In Markup
<div ng-show="$root.shouldShow"></div>
It's not a good idea to manipulate a $scope of a controller from a different controller directly. It would make your controllers very tightly coupled.
One commonly accepted way to communicate between controllers is through messages using the publish-subscribe pattern.
If your controllers are in completely separate components and you just need to notify the other controller, you can use the $broadcast/$emit and $on methods of the scope object to broadcast a message from one controller and listening to a specific message in a different controller. When an action happens that should change the shouldShow variable, you can just broadcast a message and make the other controller listen and act on it.
Root Scope API
Another common way to communicate between controllers is by using angular services, acting as a mediator between controllers.
If your controllers are part of the same component/module, and you need to share state/behavior between those, then using an angular service to encapsulate that logic and expose it would be an OK approach (services are singletons in Angular). That would be pretty simple to implement.
However, not knowing more details about your exact requirements, it's hard to design a proper solution.
Thanks to #Callum Linington for discussing the alternatives in the comments.
I am trying to call a directive from a service. I have implemented everything but my service scope is coming up empty. Normaly my functions are calling a directive from a controller. But these functions are now needed across multiple controllers so I want to put them in a service. Problem is my service cant see the functions in the directive even after doing dependency injection of the directive into the service.
export interface MyServiceScope extends IScope, directives.MyPanelScope, directives.MyPanelMenuScope {
// a bunch of properties that are not visible in the service scope, only the controller
}
// constructor here, controller does not initialize directives here just in interface and it works fine.
public serviceFunc() {
this.scope.panel.directiveFunc({ // directiveFunc undefined in service, but not in controller
template: "some/html/template",
data: {
item: data
}
});
}
So what do I do about getting the service to see the directive and call its functions?
You cannot inject a directive into a service n Angular. Actually you cannot inject a directive anywhere.
Directives are supposed to be a set of functions that strictly relate to specific DOM elements, and it is usually bad practice to use them to share code between different parts of the app. That purpose can be fulfilled by services.
If you find yourself in the situation where a service needs to call some function from a directive, I suggest that you review your code structure. I don't know the specifics of your case, but you could take the "shared" logic out of your directive into a service, inject that service into the directive and call it from there. In this way the logic called by the directive will be available to be injected to other parts of the application as well.
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.
I have created a service in angularJS that uses btford.socket-io module to interact with the server.
Since in the service I have implemented some API that I use inside angular at the moment, but for later extension of the application I also need to give access to these API outside angular scope.
So that in future one could just call the function without having the need to create controller and other stuff.
At the moment I did this for a controller
var myController = angular.element($('body')).scope().myController;
by saving the whole controller inside a scope variable.
I was wondering if it would be possible to do the same with a service.
How about:
angular.element(document.body).injector().get('MyService');
Typically not good practice. But sometimes its needed.
Note: document.body is the element Your angular application is mounted to
Another thing you might consider is 'closing' external api with angular via a factory.
eg. return your global or namespaced class or api from an angular factory.
This is essentially what angular does for you. But instead of creating the reference inside angular first and having to extract it, you'd create it outside, and register it with DI as a factory or value.