I have angular app
<body ng-app="appName">
<div class="container" ng-view=""></div>
I have routes
$routeProvider
.when('/', {
templateUrl: 'partials/main',
controller: 'MainCtrl'
})
.when('/login', {
templateUrl: 'partials/login',
controller: 'LoginCtrl'
})
I want to call a service before every route. Say for example if I have not loaded the profile data I want load profile data and store it in $rootscope. How should I do this?
You can use the $route's resolve property to call a function that will be executed prior to the route change:
From the AngularJS API Docs:
resolve - {Object.<string, function>=} - An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated. If all the promises are resolved successfully, the values of the resolved promises are injected and $routeChangeSuccess event is fired. If any of the promises are rejected the $routeChangeError event is fired. The map object is:
This is usually meant to inject the route's controller with additional parameters but there's no reason you could do more.
$routeProvider
.when('/login',{
templateUrl : 'partials/login',
controller: 'loginCtrl',
resolve : {
some_extra_controller_param : ['$route','someService',function($route,someService){
// do stuff here that you would feel necessary to have done prior to route change
someService.doSomething();
return true; // or return an object of data maybe your controller could use
]}
}
});
Of course the some_extra_controller_param will be injected into your controller as the last parameter so make sure you return something in the resolve, loginCtrl might look like this:
.controller('loginCtrl',['$scope','some_extra_controller_param',function($scope,some_extra_controller_param){
...
]});
EDIT: You may want to setup the resolve function to use promises as the route's controller will wait on promises to be "resolved" before instantiating the controller.
EDIT:
var myBeforeRouteCheck = {
something_to_be_resolved : ['$route','someService',function($route,someService){
// assuming your service runs some kind of function that returns a promise
return someService.someFunc().then(
function(data){
...do successful things...;
return somethingToInjectedParam;
},
function(){
... error ...
return false;
});
}]
};
then in your route:
.when('/login',{
...
resolve: myBeforeRouteCheck
}
Related
Given the following state in ui-router:
.state('some.state', {
url: '/some/:viewType',
templateUrl: 'myTemplate.html',
controller: 'SomeStateController',
controllerAs: 'vm',
data: {
authorizedFor: [SOME_ROLE]
}
}
I'm trying to use the "data" object for a state to help control access to authorized states. Separately, I handle the $stateChangeStart event to look at data.authorizedFor and act accordingly.
The problem, though, is that the list of authorized roles might change based on the value of :viewType. I thought I could let data:{} be a function, inject $stateParams, and handle the logic there...but that won't do.
So, I tried using the params object instead, but at the $stateChangeStart time, the :viewType is not yet accessible from $state.params or $stateParams.
Stepping through in dev tools, I noticed that $state.transitionTo.arguments is populated, but it seems awfully hacky to go that route.
params: {
authorizedFor: function($state) {
console.log($state.transitionTo.arguments[1].viewType); // has value I need
}
}
Any suggestions?
My suggestion is to use resolve to provide your controller with content or data that is custom to the state. resolve is an optional map of dependencies which should be injected into the controller.
If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the $stateChangeSuccess event is fired.
for example:
$stateProvider
.state('profile', {
url: '/profile',
templateUrl: 'profile.html',
resolve:{
'ProfileService': function(ProfileService){
return ProfileService.promise_skillRecommendation_mock;
}
}
})
The profileService code:
var app = angular.module('app').service("ProfileService", function($http){
var myData = null;
var promise_skillRecommendation_mock =
$http.get('Mock/skillRecommendation-mock.json')
.success(function(data){
myData = data;
});
return{
promise_skillRecommendation_mock: promise_skillRecommendation_mock,
get_skillRecommendation: function(){
return myData;
}
};
});
and the controller code which will use this service is:
angular.module('app').controller('ProfileController', function($scope, $http, ProfileService){
$scope.skillRecommendation = ProfileService.get_skillRecommendation();
The object in resolve below must be resolved (via deferred.resolve() if they are a promise) before the controller is instantiated. Notice how each resolve object is injected as a parameter into the controller.
by using this code, the page will be displayed only after that the promise will be resolved.
for more info please view this page: https://github.com/angular-ui/ui-router/wiki
I have a requirement to call another controller after the first one is loaded and rendered. Means I want to call controllers one-by-one, when one controller is successfully loaded and rendered then another controller is initiated.
You need to use $broadcast, using this you can notify the other controller when something changes on your first controller.
Angular provides $broadcast, $on and $emit for this.
answered here
And if above is not fits your requirement you should use AngularJS stateprovider (ui-router and define states and bound your controller.
First call state1 which has controller1, if everything goes fine within that controller call state2 which has controller2
$state.go("state2");
var routerApp = angular.module('module-name', ['ui.router']);
routerApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/state1');
$stateProvider
.state('state1', {
url: '/state1',
templateUrl: 'state1.html',
controller : 'controller1'
})
.state('state2', {
url: '/state2',
templateUrl: 'state2.html',
controller : 'controller2'
})
});
Index.html
<div ui-view></div>
ui-router example here
Controller like
routerApp.controller("controller1", ['$state', function($state){
//after
$state.go("state2");
}]);
I have a file "controller.js" Which has homeController and sectionController defined.
in route.js
when('/home', {
templateUrl: 'view/main/home.html',
controller: 'homeController',
access: {
requiredLogin: false
}
}).
when('/section/:menu', {
templateUrl: 'view/main/section.html',
controller: 'sectionController',
access: {
requiredLogin: false
}
})
but when call to /section/ is made. I could still see $http request defined in homeController being made. Can anybody please tell me the reason and how to tackle this. A proper binding of controllers to the views.
Make sure the controller is not being instantiating in your html with ng-controller.
Apart from this, there was a use of $interval. In each cycle, a call was made. When a change in route occurred $interval was not destroyed and remain active. Found an explanation at angularjs $interval being called multiple times now the issue is resolved.
I like the ability for Angular's router to 'resolve' data for me before transferring control to a controller. How can I show something to the user (spinner, "loading...", etc.) while waiting for a route's resolve function to complete, in the case of an ajax call getting run for the resolve function?
Here's an example from a router that shows what I mean:
.when('/users/:userId/profile', {
templateUrl: 'views/profile.html',
controller: 'ProfileCtrl',
resolve: {
userProfile: ['$route', 'ProfileService', function ($route, ProfileService) {
return ProfileService.loadProfile($route.current.params.userId);
}]
}
})
As I understand it, "ProfileCtrl" does not get created until "userProfile" has been resolved. That means I can't put code there to run while waiting for the resolve function to complete. In my case "ProfileService.loadProfile" makes and HTTP request, so for the sake of this example, let's say it takes a few seconds to return.
What's the recommended way to show something to the user while waiting for this?
You can use $routeChangeStart and $routeChangeSuccess to set some boolean that is used to display a loading animation or whatever in your view:
angular.module('myApp').run(function($rootScope) {
$rootScope.$on('$routeChangeStart', function() {
$rootScope.isLoading = true;
})
$rootScope.$on('$routeChangeSuccess', function() {
$rootScope.isLoading = false;
})
})
in your template you'd have something like:
<div class='loading' ng-show='isLoading'></div>
Or since that is linked to a template which may or may not be available, put class on the page body:
<body ng-class='loading: isLoading'>
</body>
and style it however you like.
As per https://docs.angularjs.org/api/ngRoute/service/$route 'Once all of the dependencies are resolved $routeChangeSuccess is fired.' There is also routeChangeError if you want to tell users when there is a problem with your ajax/resolve. ui-router has analogous stateChangeStart etc.
I have a AngularJS application and have a requirement to initialize data from a REST API before the controller initializes. I use the "resolve" in the routeProvider and also injected the relevant value in the controller in order to make this data available. The code snippets are as follows:
RouteProvider code snippet:
myApp.config(function($routeProvider) {
$routeProvider
....
.when('/account', {
templateUrl : path + 'admin/js/pages/inputs/account.html',
controller : 'mainController',
resolve: {
data: function() {
return $http.get(api_path + 'dashboard/get_accounts');
}
}
})
myApp.controller('mainController', function($scope,$http, data, $routeParams, DataService) {
...
console.log(data);
}
The console is supposed display the data by I get the following error " Error: [$injector:unpr] Unknown provider: dataProvider <- data "
Your help much appreciated.
It's because the data provider has not instantiated yet and it is instantiating the controller before the provider is ready, coming through as an undefined and unknown provider.
Try something like this that returns a promise:
myApp.config(function($routeProvider, $q) {
$routeProvider, $q
....
.when('/account', {
templateUrl : path + 'admin/js/pages/inputs/account.html',
controller : 'mainController',
resolve: {
data: function() {
return $q.all($http.get(api_path + 'dashboard/get_accounts'));
}
}
})
Now, the controller won't instantiate until the promise has resolved completely. As per the documentation for $routeProvider and how it handles promises in the resolve.
$routeProvider on Angular's website
resolve - {Object.=} - An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated. If all the promises are resolved successfully, the values of the resolved promises are injected and $routeChangeSuccess event is fired. If any of the promises are rejected the $routeChangeError event is fired.