Broadcast event when state loaded and controllers are initiated - angularjs

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).

Related

How to stop angular from creating multiple instances of a controller every time we use $state.go

I am using UI router for angular routing. Everytime $state.go() is called a new instance of the controller is being created. I have $rootScope events here. So everytime I want to print any log (using console.log()) or trigger the event from outside the controller it is executing multiple times i.e. the same number of time as the instances of the controller. How can I resolve this issue? I want the execute only one time. Any kind of help is appreciated. Thanks for answer in advance.
code snippet.
$rootScope.$on('connect_device',function () {
connect_device($rootScope.mac_address,$rootScope.device_name);
});
From the different controller I am emitting.
$rootScope.$emit("connect_device")
You are missing some destroyhandling on your controller instance.
The events sunscribed to the $rootScope have to be unsubscribed upon destroy. The controller are indeed created on $state.go, and your eventlistereners are attached each controller creation phase. Not destroying these eventlisteners will cause such behaviour.
Please see following sample code to indicate how you can solve your problem.
angular.module('moduleName')
.controller('controllerName', ['$rootScope', '$scope', function ($rootScope, $scope) {
var cleanUpFunc = $rootScope.$on('eventName', function {
// listener actions
});
$scope.$on('$destroy', function() {
cleanUpFunc();
});
}]);
By calling cleanUpFunc() in $destroy, your event listener for the eventName event will be un-subscribed and you will no longer be leaking memory when your controller gets cleaned up.
When there is no destroyment of events, even when the controller is already destroyed, the eventlisteners will still be active. And as a possible result you will notice that this can invokes multiple functions upon one event.
Off course without any real code examples, the following illustrates how you have to deal with events upon controller destroy.

invoking directive controller method from another directive's controller

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');

$viewContentLoaded is executed before initializing root scope

Method on $viewcontentloaded is firing asynchronously. To detail my problem, I have a variable in root scope i.e. my Main controller, which need to be initialized before my view controller loads. In module.run I am calling a sync function to initialize $rootScope.session. And In my view controller of a route, I am checking the status of session in afunction that is called like
$scope.$on('$viewContentLoaded', function() {
$scope.initialize();
});
But some times on page refreash, I am getting an undefined value for $rootScope.session, as It may have initialized later. So, Is there any way to make this synchronous like rootscope will be initialized before view loads. And for curiosity, how it will affect, if I call the $scope.initialize(); normally in my controller, in $viewContentLoaded or in $routeChangeSuccess.
Thanks in advance.
So, Is there any way to make this synchronous like rootscope will be initialized before view loads.
Use the $controller service to manually create the controller, as in a unit test.
$controllerProvider.register('FooCtrl', FooCtrl);
ctrl = $controller('FooCtrl', {$scope: scope});
Or $broadcast a custom event from the main controller down to the child:
function mainCtrl($rootScope)
{
$rootScope.$broadcast('abc');
}
function secondCtrl($scope)
{
$scope.$on('abc', function(event) { $scope.initialize(); });
}
Or use a try/catch block and a recursive call with a timer.
These are more or less the steps that you would take to implement lazy loading in AngularJS. In summary, you would first define your app module to keep instances of the relevant providers. Then you would define your lazy artifacts to register themselves using the providers rather than the module API. Then using a ‘resolve’ function that returns a promise in your route definition, you would load all lazy artifacts and resolve the promise once they have been loaded. This ensures that all lazy artifacts will be available before the relevant route is rendered. Also, don’t forget to resolve the promise inside $rootScope.$apply, if the resolution will be happening outside of AngularJS. Then you would create a ‘bootstrap’ script that first loads the app module before bootstrapping the app. Finally, you would link to the bootstrap script from your ‘index.html’ file.
References
AngularJS source: controllerSpec.js
Ifeanyi Isitor: Lazy Loading In AngularJS
AngularJS Lazy Loading with Require.js
Split Large AngularJS Controllers using the Mixin Pattern

Forcing a digest in Angular

At some point after a user action I would like to cause a digest to occur, so the UI reflects a change in the data-model backing it.
I have a service that performs some change in a callback (asynchronously).
I understand that $scope only makes sense in the context of a controller. Can I achieve the same effect by performing $apply() on the $rootScope?
I have seen code that checks for $$phase or similar related to avoiding digest errors, what checks should I perform in order to trigger a digest safely?
See this answer: Running $apply on $rootScope vs any other scope
You can call $rootScope.$apply() outside of a controller (i.e. in a service) in order to trigger a digest loop.
Alternatively, you could consider using $broadcast and $on to send a notification to other parts of your app when something needs refreshing. (See Understanding Angular’s $scope and $rootScope event system $emit, $broadcast and $on)
// in a service
$rootScope.$broadcast('myCustomEvent', {
someProp: 'foobar'
// other data
});
// in a controller or another service
$scope.$on('myCustomEvent', function (event, data) {
console.log(data);
// do something with this event
});

How to catch an event in controller which is thrown by an directive in Angular?

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).

Resources