Run a function every time an Angular controller is loaded - angularjs

I want to run a function every time any Angular controller is loaded.
The best way I have found is to attach this handler in the first controller loaded on my site:
$rootScope.$on('$stateChangeSuccess',
function(event, toState, toParams, fromState, fromParams){
myFunction();
}
);
Is there a better alternative?

If you just want to execute a function every time the controller is loaded, the following would suffice.
var init=function(){
//your code goes here
}
init();
But in most cases, you would require a function to be executed only on load of a specific page (route). In this case, use the following.
if ($location.url() == "/your/page/url")
{
//Your function code or function call goes here
}
Edit: For your specific need, the code for Google Analytics can be handled efficiently in Angular $http Interceptor. Refer Angular $http Interceptors

I would prefer moving that function into the resolve section of your $stateProvider. Your state config could look something like
.config(['$stateProvider', function($stateProvider) {
$stateProvider
.state('myRoute', {
url: '/myUrl',
templateUrl: '/template.html',
controller: 'MyController',
controllerAs: 'vm',
resolve: {
dummyVariable: function(MyService, $stateParams) {
MyService.functionToBeCalled();
return true;
}
}
});

Related

Capturing and using URL in angular UI-router

I have a state:
.state('series', {
url: '/{series}/',
template: '<div configurator-list></div>',
resolve: {
stateService: 'ConfiguratorStateService'
},
params: {
series: '{series}'
},
controller: 'ConfiguratorListController',
ncyBreadcrumb: {
label: '{series}',
parent: 'home'
}
});
I'd like to use actual value for {series} that is in the URL to update a few things. I'm lost and haven't had any luck searching. Everything takes me to the UI-router page, but I don't see any concrete examples there.
You need to listen for any of the UI-Router state change events and use the parameters from that function to obtain it. Add a listener in upon your app run (substitute app name for 'myApp') which is detailed below...
(function() {
angular.module('myApp')
.run(runOptions);
runOptions.$inject = ['$rootScope', '$state'];
function runOptions($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
if (toState.name === 'series') {
//output the parameter here... since the series parameter is actually the string of the url, use the url property
console.log(toState.url);
}
});
}
})();
There are other state change events you can use as well, depending on your intended use: https://github.com/angular-ui/ui-router/wiki#state-change-events
OR
What sounds more relevant with further reading of your question, modify your route definition...
Change url: '/{series}/' to '/:series'
Then, in your controller....
angular.controller('ConfiguratorListController', function($stateParams) {
var vm = this;
vm.series = $stateParams.series;
}

inject resolve into toParams on $stateChangeStart

I have a config file which contains a state that looks like the following:
$stateProvider.state('home', {
url: '?data',
abstract: true,
resolve: {
data: ['MyModel', '$stateParams', function (MyModel, $stateParams) {
return MyModel.getData($stateParams);
}]
}
});
and then a module file that looks like this:
App.module.run(['$rootScope', '$state', function ($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
where toParams would return object{data:undefined}. I know that the resolve works and should have data. How do I correctly pass the resolve from my state into the $stateChangeStart and then access it?
I have tried injecting my data resolve manually into my module file, but it returns undefined. I have tried setting the params attribute to see if that was the way to go:
resolve: {
data: ['MyModel', '$stateParams', function (MyModel, $stateParams) {
return MyModel.getData($stateParams);
}]
},
params: {
'foo':'bar', // this would be available in toParams
'data': //can i put my data here?
}
You should simply be able to inject MyModel into your run function, and read it the same way you read it inside your resolve process. Example run function (assumes your getData function returns a promise):
App.module.run(function($rootScope, MyModel) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
// get the same data your resolve is using (you can also pass in toParams or the like)
MyModel.getData(toParams).then(function(data){
// do something with your data here inside $stateChangeStart
});
});
});
This is what makes injection great, you simply inject something when you want to use it.

call function inside a controller with ui-router

I am looking for a solution to call a function inside a controller. How could i achieve this.
State
.state('confirm.registration',
{ url: "/confirm/registration",
controller:'userController',
onEnter:'confirmregistration()',
permissions:{}
})
Controller
$scope.confirmregistration = function(){
//do something
}
This works. But want to know whether is this the right approach.
app.js
.state('confirmregistration',
{ url: "/confirm/registration/:registrationseed",
controller:"userController",
permissions:{}
})
Controller
if($stateParams.registrationseed){
console.log($stateParams.registrationseed);
}
I'd suggest you to put $broadcast event inside your onEnter function, that would be broadcasted in $rootscope and that event can be listen by using $on inside controller.
Config
.state('confirm.registration', {
url: "/confirm/registration",
controller: 'userController',
onEnter: function($rootScope, $timeout, $stateParams) {
//used $timeout to load after controller gets initialized
//so that $on event should get define before broadcasting
$timeout(function() {
$rootScope.$broadcast('eventBroadcasted', $stateParams.registrationseed);
})
},
permissions: {}
})
Controller
$scope.$on('eventBroadcasted', function(event, data) {
//you can get $route parameter inside data itself
});
I think this would be approach, rather than what you are doing. Thanks.

Need to catch ui.router state and its url when the url changes

I wish to attach a listener to URL changes in the ui.router module I use in an AngularJS app, because I need to get the new state and its url when the url changes. In order to customize the url change listener, I've done the following, but it's not working (yet):
in the config section of ui.router, I added this line:
$urlRouterProvider.deferIntercept();
and after the config(), I have the following code, hoping to cactch the ui.router's state and its url
config(
//ui.router's config ommitted....
).run(function($rootScope, $urlRouter) {
$rootScope.$on('$locationChangeSuccess', function(e) {
console.log(e);
e.preventDefault(); //prevent the default handler
$urlRouter.sync(); //once the interception is done, sync the current url
$urlRouter.listen();
})
});
I am not sure if this is possible to do with ui.router. Or there is a much easier way to get the state and its url?
I am not 100% sure what is the real requirement behind. But with UI-Router, we should use $stateChangeStart event I'd say.
There is an example, a working plunker
$rootScope.$on('$stateChangeStart', function (event, toState, toParams
, fromState, fromParams)
{
console.log(event)
console.log(toState)
console.log(toParams)
console.log(fromState)
console.log(fromParams)
console.log(toState)
console.log($location.url())
});
Check it here
The $urlRouter and its .deferIntercept() and .listen() or more useful for declaring states in a .run() phase
// config
.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProviderRef = $stateProvider;
$urlRouterProvider.otherwise('/home');
$urlRouterProvider.deferIntercept();
// run
.run(['$rootScope', '$urlRouter',
function($rootScope, $urlRouter) {
$stateProviderRef
.state('home', {
url: "/home",
templateUrl: 'tpl.html',
})
$urlRouter.listen();
}
]);

initial $stateParams empty?

Plunker:
I am using UI-Router for a simple single page application. It is a just a table that you can sort and filter. When you apply a filter, like ordering a column in ascending order, the URL updates to something like:
www.domain.com#/column/'column name'/desc/false/
I then grab the paramters using $stateParams, and update the table accordingly.
$scope.$on('$stateChangeSuccess', function (evt, toState, toParams, fromState, fromParams) {
$scope.sorting.predicate = toParams.column;
$scope.sorting.reverse = boolVal(toParams.sort);
});
However, if I were to visit www.domain.com#/column/'column name'/desc/false/,
the table doesn't load with the data filtered. I am trying the following (after injecting $stateParams to my controller):
$scope.$on('$viewContentLoaded', function (event, viewConfig) {
console.log('VCL stateParams: ' + angular.toJson($stateParams));
});
However, stateParams is always empty regardless of the URL (only on viewContentLoaded). I can't seem to get the parameters of the URL on page load. Am I doing something incorrectly?
Here is the state config:
app.config(['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
$stateProvider.state("name", {
url: '/column/:column/desc/:sort/',
controller: 'Ctrl',
resolve: {
logSomeParams: ['$stateParams', '$state', function ($stateParams, $state) {
console.log($stateParams);
console.log(this); // this is the state you transitioned From.
console.log($state); // this is the state you transitioned To.
}]
}
});
$urlRouterProvider.otherwise('/');
}
]);
Here is how the state change is called:
<a ui-sref="name({column:'\''+column+'\'',sort:sorting.reverse})"
ng-click="sorting.predicate = '\'' + column + '\''; sorting.reverse=!sorting.reverse;">
{{column}}</a>
Here's a few options I'd investigate further.
Option 1: $rootScope listener
$scope.$root.$on('$stateChangeSuccess', function (evt, toState, toParams, fromState, fromParams) {
$scope.sorting.predicate = toParams.column;
$scope.sorting.reverse = boolVal(toParams.sort);
});
$scope.$root.$on('$viewContentLoaded', function (event, viewConfig) {
console.log('VCL stateParams: ' + angular.toJson($stateParams));
});
Option 2: Resolve objects
In your state definition, try to add this:
resolve: {
logSomeParams: ['$stateParams', '$state', function ($stateParams, $state) {
console.log($stateParams);
console.log(this); // this is the state you transitioned From.
console.log($state); // this is the state you transitioned To.
}]
}
Please give either of those a go and let me know what results you get.
The resolve object should run prior to $viewContentLoaded, but since all you are really interested in are the stateParams in your $viewContentLoaded callback, might aswell put it in the state definition.
Good luck,
Kasper.
Edit:
Try giving your state a parent state with the resolve object.
Like this:
$stateProvider.state('parentState', {
url: '',
abstract: true,
resolve: {
logSomeParams: ['$stateParams', '$state', function ($stateParams, $state) {
console.log($stateParams);
console.log(this); // this is the state you transitioned From.
console.log($state); // this is the state you transitioned To.
}]
}
});
$stateProvider.state("name", {
url: '/column/:column/desc/:sort/',
parent: 'parentState',
controller: 'Ctrl'
});
Let me know how that works out for ya.
Figured it out, quite unfortunate how simple the answer was. I needed to inject $state. I only injected $stateParams. You need both. Hope this saves someone some hair pulling and screaming.

Resources