$emit not working in controllers in different html pages - angularjs

I have 2 controllers and one service.From one controller calling service and result $emit to another controller in another page .but its not working.Please help.Thanks in advance.
In first controller
routerApp.controller('DetailCtrl',['$scope''ChefService','$rootScope',function($scope,ChefService,$rootScope){
$scope.Fn_ClickChefProfile = function(chefid){
ChefService.GetChefProfile(chefid).then(function(d){
$rootScope.$emit("CallToChef",d);
});};]});
In second controller
routerApp.controller('ChefCtrl',['$scope','$rootScope','ChefService',function($scope,$rootScope,ChefService){
$rootScope.$on("CallToChef",function(event,data){
$scope.Fn_GetChefProfile(data);
});
$scope.Fn_GetChefProfile = function(data){
console.log(data);
};}]);

Take a look at this answer
$emit dispatches an event upwards (to parent controller) ... $broadcast dispatches an event downwards (to child controller)

Related

How to trigger an event in one view from a different view?

I am trying to open an Angular accordian in the header.html by clicking a button which is in the body.html. Essentially triggering an event in one view from a completely different view. Does anyone have any idea how to do this in Angular?
What you can do is using events to let your accordion directive know that something happend or use a shared service. Considering the performance, it does not make a huge difference, but only if you use $emit instead of $broadcast since the event fired via $emit bubbles up your scope hierarchy and $broadcast sends the event down. Also make sure to fire the event on the $rootScope, so it won't event bubble up anymore.
So you in case you want to use events for you could have a method on your component that fires the event via $emit on the $rootScope as follows:
function openAccordion() {
$rootScope.$emit('on-accordion-open', null);
}
You could then use this in your view, e.g. in body.html. Remember that function above is part of another directive / component or controller.
<button ng-click="vm.openAccordion()">Open Accordion</button>
Also note that I assume you are using controllerAs syntax (set to vm).
In your accordion directive you can then hook up listeners to several events for example the on-accordion-open:
$rootScope.$on('on-accordion-open', function() {
// Open the accordion
});
The other soltuion is to use a shared service. In this case I would create a AccordionServce that is aware of all instances of accordions. The service could look like this:
angular.module('myApp').service('AccordionService', function() {
var accordions = {};
this.addAccordion = function(name, accordion) {
accordions[name] = accordion;
};
this.removeAccordion = function(name) {
delete accordions[name];
};
this.getAccordion = function(name) {
return accordions[name];
};
});
In your accordion's controller you then add the accordion to the AccordionService via
accordionService.addAccordion('myAccordion', this);
The this in the snippet above is refering to the accordion controller. Thats important because if you then get an accordion in your component in the body.html, you'll get the controller instance and can call methods like open.
So in your body component you can then inject the AccordionService and get the accordion to call a method:
accordionService.getAccordion('myAccordion').open();
Make sure to define open on the accordion's controller.

Access ng-model data outside of the controller

I have written the below code
<span ng-controller="calanderCtrl">
<input type="text" ng-model="onDate">
</span>
<pre>user.name = <span ng-bind="onDate"></span></pre>
I know its outside of the ng-controller so i am not able to bind the data, but my application requires calanderCtrl controller. I want to put this value to scope so that i can use it inside other controllers also. How do i do this?
You could use a publish subscribe pattern for this. That way you avoid putting the variable on the rootscope.
function Ctrl($scope) {
$scope.onDate = "12/01/2015";
$scope.$watch('onDate', function(newValue, oldValue) {
$scope.$emit('onDateChanged', newValue);
});
}
function Ctrl2($scope, $rootScope) {
$scope.onDate = "";
$rootScope.$on('onDateChanged', function(event, value) {
$scope.onDate = value;
});
}
Your controller will get called when your template loads.
<span ng-controller="Ctrl">
<input type="text" ng-model="onDate">
</span>
<pre>user.name = <span ng-controller="Ctrl2" ng-bind="onDate"></span></pre>
Now how does it work:
Angular does not share scopes. Each controller has its own seperate scope.
So in order to keep our child scopes up to date we need to somehow throw an event on which our children subscribe to. This can be done in two ways.
$scope.$emit or $rootScope.$broadcast
The difference between the two is subtle.
$scope.$emit wil send the event up the chain. so for instance consider this scope hierarchy.
rootscope
scope1 ---> subscribes to the emit $scope.$on
scope2 ---> performs a $scope.$emit
scope3 ---> subscribes to the emit $scope.$on
only scope1 will catch the event. since $scope.$emit goes up the chain.
this is a way to only update specific scopes. although what is mostly done is this.
rootscope
scope1 ---> subscribes to the emit $rootScope.$on
scope2 ---> performs a $scope.$emit
scope3 ---> subscribes to the emit $rootScope.$on
we inject $rootScope in the controller of scope1 and scope3 and subscribe to the emit on the rootscope. Since the rootscope is the highest scope it will always catch the $emit from scope2. This is a way to only send the event to specific controllers wich subscribe on the rootscope.
lastly we can also do this:
rootscope
scope1 ---> subscribes to the emit $scope.$on
scope2 ---> performs a $rootScope.$broadcast
scope3 ---> subscribes to the emit $scope.$on
we are now shouting on the rootscope and instead of moving up like emit, broadcast works down the chain. This is the equivalent of shouting in a room and everyone who doesnt have his ear protectors on will hear it. in essence everyone who subscribes on their local scope to the event that broadcast is sending

Angularjs destroy is not working

http://plnkr.co/edit/UfQJU661pQR0DMY3c61t?p=preview
I got above code from AngularJs site and only thing I have added a button to delete a Div where we have controller but after delete no destroy method called as I have put alert in Directive and Controller.
element.on('$destroy', function() {
alert('destroy directive interval');
$interval.cancel(stopTime);
});
and
$scope.$on('$destroy', function() {
alert('destroy controller interval');
// Make sure that the interval is destroyed too
$scope.stopFight();
});
please suggest.
Thanks
The main thing to be noticed
When element.remove() is executed that element and all of its children will be removed from the DOM together will all event handlers attached via for example element.on.
It will not destroy the $scope associated with the element.
So you need to manually trigger scope.$destroy();
First get the scope of element:-
var scope = angular.element(document.getElementById("mainDiv")).scope();
Second remove the element from dom:-
$('#mainDiv').remove();
Third destroy scope manually:-
scope.$destroy();
Plunker
You're doing it outside of angular's context.
<button id="btn" onclick="DeleteMainDiv()">DeleteDiv</button>
So in your DeleteMainDiv() function
function DeleteMainDiv() {
alert('Controller div going to remove');
//debugger;
var scope = angular.element(document.getElementById("mainDiv")).scope();
$('#mainDiv').remove();
scope.$destroy();
}
This will trigger the destroy functionality.
But I don't see a need of it. Angular will automatically run the $destroy event handler when the route changes or directive no longer required.
DEMO

What is .$on() in AngularJS

I got this code $rootScope.$on('abc',function(event, next, current){ }); in a tutorial.
My question is what is .$on()? If it is a function, then why is it preceded by $?
$on is related to $broadcast and $emit - which is a way to trigger code from other places.
The first thing about $on you should know is that it's a method of $scope
The second thing you should know is $ prefix refers to an Angular Method, $$ prefixes refers to angular methods that you should avoid using.
Now lets get into detail about what $on is.
Below is an example template and its controllers, we'll explore how $broadcast/$on can help us achieve what we want.
<div ng-controller="FirstCtrl">
<input ng-model="name"/>
<button ng-click="register()">Register </button>
</div>
<div ng-controller="SecondCtrl">
Registered Name: <input ng-model="name"/>
</div>
The controllers are
app.controller('FirstCtrl', function($scope){
$scope.register = function(){
}
});
app.controller('SecondCtrl', function($scope){
});
My question to you is how do you pass the name to the second controller when a user clicks register? You may come up with multiple solutions but the one we're going to use is using $broadcast and $on.
$broadcast vs $emit
Which should we use? $broadcast will channel down to all the children dom elements and $emit will channel the opposite direction to all the ancestor dom elements.
The best way to avoid deciding between $emit or $broadcast is to channel from the $rootScope and use $broadcast to all its children. Which makes our case much easier since our dom elements are siblings.
Adding $rootScope and lets $broadcast
app.controller('FirstCtrl', function($rootScope, $scope){
$scope.register = function(){
$rootScope.$broadcast('BOOM!', $scope.name)
}
});
Note we added $rootScope and now we're using $broadcast(broadcastName, arguments). For broadcastName, we want to give it a unique name so we can catch that name in our secondCtrl. I've chosen BOOM! just for fun. The second arguments 'arguments' allows us to pass values to the listeners.
Receiving our broadcast
In our second controller, we need to set up code to listen to our broadcast
app.controller('SecondCtrl', function($scope){
$scope.$on('BOOM!', function(events, args){
console.log(args);
$scope.name = args; //now we've registered!
})
});
It's really that simple. Live Example
Other ways to achieve similar results
Try to avoid using this suite of methods as it is neither efficient nor easy to maintain but it's a simple way to fix issues you might have.
You can usually do the same thing by using a service or by simplifying your controllers. We won't discuss this in detail but I thought I'd just mention it for completeness.
Lastly, keep in mind a really useful broadcast to listen to is '$destroy' again you can see the $ means it's a method or object created by the vendor codes. Anyways $destroy is broadcasted when a controller gets destroyed, you may want to listen to this to know when your controller is removed.
The previous answer is a very good one. I'd like only to add a short remark: this $on kind of listener has a very important property: can be canceled (stoped).
I'll explain what do I mean:
The html:
<div ng-controller="FirstCtrl">
<input ng-model="name"/>
<button ng-click="register()">Register </button>
</div>
<div ng-controller="SecondCtrl">
Registered Name: <input ng-model="name"/>
</div>
<hr/><br/>
<button ng-click="disableEvents()">Disable events</button>
And the controller:
app.controller('SecondCtrl', function($scope){
$scope.cancelOn = $scope.$on('BOOM!', function(events, args){
console.log(args);
$scope.name = args; //now we've registered!
});
// this will cancel the event listening....
$scope.disableEvents = function(){
console.log('Canceling the event listener: ', $scope.cancelOn());
}
});
If you press the "Register" button, you can see it communicate with the second controller. Now press the "Disable events" button. This will cancel the listener, the $on(...). Now, if you press again the "Register", you'll notice that the listener is no longer listen for this kind of event.

AngularJs: to Propagate event between directives by $emit

I have under one controller two directives :
<div ng-controller="ctrl">
<div data-dy-items-list items="items"></div> // FIRST DIRECTIVE
<div data-dy-subitems-list subitems="subitems"></div> //SECON DIRECTIVE
</div>
In the Second directive template, I have one button and in the directive.js file in the controller section I did this :
$scope.clickButton= function () {
......
$scope.$emit("UPDATE_PARENT","updated");
}
In the first directive, I would like to do this in the controller section:
$scope.update = false;
$scope.$on("UPDATE_PARENT", function (event,message){
$scope.update = true;
console.log('update: ' + $scope.update);
});
But it doesn't work!!!
$emit dispatches an event upwards through the scope hierarchy. Your directives are siblings and thus $emit won't work.
The solution might be to $broadcast an event from a parent scope. Do it from ctrl if that's an option for you, or inject $rootScope to the directive and do $rootScope.$broadcast from there:
$rootScope.$brodcast("UPDATE_PARENT","updated");
Mind that $broadcasting events from $rootScope might seem to be an anti-pattern for AngularJS. It strongly depends on the usecase. There are other solutions to your problem:
One of them is to create a parent directive for both of your directives.
Another one is to use an intermediatory service which will hold values. Then you can do $watch on the service data and react accordingly.
You can $emit the event to the ctrl and then ctrl will $broadcast it down to the other directive.
Choose whatever fits your needs best.
I just got same problem.
Event between two directives was now passed, not with scope.$emit, not with scope.$broadcast.
After looking around, I did this trick,
Use scope.$parent.$broadcast with your $parent scope:
Directive 1:
scope.$parent.$broadcast('NO_LIVE_CONNECTION', {});
Directive 2:
scope.$on('NO_LIVE_CONNECTION', function (event, params) {
console.log("No live DB connections ...");
scope.state.showErrorMessage = true;
});

Resources