I have a directive DirectiveA which has a method x defined inside its controller. I need to invoke this method from another directive's (DirectiveB) controller.
The problem is with the app I work on because when you need to go to a specific route, you basically invoke another directive which is then rendered by Angular. What I am looking for is to find a way to call x method after the page was rendered. That x is going to display an element on my page when it is called.
Can you help me with this?
You might want to use events, you can fire event up the scope by using $emit, or down by using $broadcast. You catch/subscribe the event using the $on listener.
So, on your directive's controller you'll listen to the event (You can also subscribe $rootScope but then you need to manually unbind the listener upon directive destroy)
$scope.$on('mySpecialEvent', function (event, data) {
... DO YOUR STUFF ...
});
And on DirectiveB you'll fire the event when you want to execute the action in DirectiveA's listener:
$rootScope.$broadcast('mySpecialEvent', 'HI');
Related
According to this answer I just want fire event from controller to another
Call a method of a controller from another controller using 'scope' in AngularJS
$scope.$on("myEvent", function (event, args) {
$scope.rest_id = args.username;
$scope.getMainCategories();
});
And in the second controller you'd just do
$scope.initRestId = function(){
$scope.$broadcast("myEvent", {username: $scope.user.username });
};
But I don't use $scope in my application, just controllerAs and this. There is a way to fire event without inject the scope ? Or should I inject $scope anyway ?But I read in another answer that using both scope and controllerAs is bad practice.
It's not possible to register an $emit or $broadcast event without $scope or $rootScope being injected in the controller.
It is indeed bad practice to use $scope variables and functions since the instance of your controller is already injected inside the $scope with the controllerAs syntax.
But there is not other choice than injecting scope objects if you want to use these events.
However you shouldn't use $emit or $broadcast events just to share data. These events are used for application wide information (like user has logged in or logged out...etc.)
A good practice when using angular events is to prefer $rootScope.$emit because $scope relies on the hierarchy of your components.
For example:
$scope.$emit will emit to the parent component.
$scope.$broadcast will broadcast to children components.
Then $rootScope.$broadcast will broadcast events to the rootScope as well as the scope (which may make your code messy real quick)
$rootScope.$emit is to be preferred as it registers the event application wide and makes it available to the rootScope only. ($rootScope.$on)
Another good practice is to unbind your custom events.
Whenever a component or directive is unloaded/destroyed, the event listener will still reside inside rootScope resulting in possible memory leaks.
To unbind an event:
var unbind = $rootScope.$on('logout', function(event, data) {
console.log('LOGOUT', data);
});
$scope.$on('$destroy', unbind);
I think that generally overuse of $broadcast and $emit is a bad practice too. If you don't want to use the $scope, why not moving the logic handled by events to a service?
I need to broadcast an event when state is fully loaded and controllers are initiated. I'm using AngularJS v1.3.15 and ui-router v0.2.11.
I'm trying to use $stateChangeSuccess event, but the controller doesn't grab the broadcasted event, because it hasn't been iniatated when the event is broadcasted.
In my module run method I have:
$rootScope.$on('$stateChangeSuccess', function (event) {
$rootScope.$broadcast('someEvent');
});
My controller has the following:
$scope.$on('someEvent', function () {
$scope.getList();
});
I tried using $viewContentLoading and $viewContentLoaded but because I have nested controllers/views, it fires multiple times, so $scope.getList() runs multiple times.
Any solution to this problem?
In your case there is no need to put a stateChange listener.
angular ui-router provide a feature for your use case: resolving a controller on state change see ui-router on github.
Further more you should use a service returning a promise, since your api call is async, and then inject the service in the controller. See the "resolve" section on the same page that answers your needs.
I solved this by catching the someEvent error and call the $scope.getList() method, but also calling the $scope.getList() method when the controller is initiated (at the bottom in the controller).
As the title asks. Are they opposite?
Also how can I remove an event from the $rootScope based on its name?
A simple example would be also welcome.
$destroy is both an event that each scope would listen to and also a method on the scope to trigger that event manually.
It is primarily used to do any actions that you want while that scope is getting destroyed. Instances of scope getting destroyed are:
Switching views via ng-view, the previous controller scope gets destroyed.
A directive is instantiated and that element is removed from the DOM, this applies to all directives, within angular framework and custom.
While on the other hand $broadcast is just used to trigger events which are in current level or child level scope.
They are not the opposite.
For the second question on how you can remove the event from $rootScope.
Lets say you have refined an event listener:
$rootScope.$on("myEvent", function () {
//some code
});
Each event listener returns a deregister function. So you can just use something like:
var removeMyEvent = $rootScope.$on("myEvent", function () {
//some code
});
Whenever you want to remove the event listener you just call the deregister function.
removeMyEvent();
That should do the trick!
I'm having some trouble sending and catching events in Angular.
Consider this plunker: Plunk..
As you can see I have two controllers and a directive. The directive 'lives' in controller 1. When something happens in the directive, I want to catch this event in controller 2. As you can see in the plunk, nothing is logged to the console; the event is not catched.
I've also tried to use a service. In this scenario I created a method on the service and when this method is called, I throw the event. Just like in the Plunk, I listen for the event in Controller 2, but this also didn't work.
Where it all comes down to is that I want to invoke a method on another controller..
In you Plunker, second controller (SecondController) is registered, but it is never actually initialized. So that's why your listener never logs the event.
It's not clear how, where and when you are using using your second controller, but if you initialize it with either ng-view (through routes) or ng-controller, than its listener will observe the event.
PLUNKER
Each time you visit a route, the associated controller (and a $scope) are (re)created.
Since you mentioned that Controller 2 is associated with a route, it will only exist when you visit that route (as #Ajay and #Stewie already mentioned), so you can't catch an event in that controller.
I suggest you create a service with some API/methods to do the following:
record that an event triggered
check to see if an event triggered
clear an event
Your directive would call the method to record the trigger.
Your Controller 2 would call the check method when it is created (and then probably call the clear method if it finds the trigger set).
Is it recommended to always hang on to the $on method, as in
var dereg = $scope.$on(...)
And later
$scope.$on("$destroy", function() { dereg(); });
Or is this only needed for certain situations? Same question for $watch
AFAIK, you should only use $destroy event when your app may have memory leak issues or zombie events.
$destroy is useful when you pass some of the directive's variables to another directive or controller, and after the element gets removed, yo want to remove it from the reference.
For instance, you may want to use $destory when your directive creates event handlers on global DOM elements and the element with the directive gets removed.
let's say this is a linking function inside a directive:
function myEventListener(){console.log('scroll!');}
$window.bind('mousewheel',myEventListener);
scope.$on('$destroy',function(){$window.unbind(myEventListener)});
If you don't unbind your event listener, then after the element with the directive gets removed, you will still have scroll! messages when you scroll.