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.
Related
I have defined a factory auth as follows:
app.factory('auth', [function () {
var auth = {};
auth.getUser = function () {
... ...
// get user from cookie, cache, history, etc.
}
return auth
}]);
For many pages, I want to always run auth.getUser() before displaying them. To this end, at the moment, I use resolve, eg:
.state('home', {
url: '/home',
templateUrl: '/htmls/home.html',
controller: 'MainCtrl',
resolve: { getUser: ['auth', function (auth) { return auth.getUser() }]}
})
The pitfall of this implementation is I have to write lots of resolve. Thus, I am wondering if there is a way to implement this rule in controllers rather than state. Could anyone help?
Try checking the condition in
$rootScope.$on('$stateChangeStart', function (event, toState) {
//condition here and $state.go('')
});
I'm not sure if this works.
use angular run function
app.run(function() {
alert(1);
});
see http://jsfiddle.net/ysq3m/
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;
}
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;
}
}
});
I have a question: When I'm in a page I want return to previous page. I use the $routeProvider. How can I read the previous url?
I try to use this code in my controller but doesn't work...
angular.module.controller('myController',
function($scope, $routeParams, $http, $location, $rootScope) {
$rootScope.$on("$routeChangeSuccess",
function (event, current, previous, rejection) {
console.log(previous);
$location.path('PREVIOUS PATH');
});
});
How can I read the previous path? Thanks!
I am not fully sure, what you want to achieve. So I would suggest, check this before you go your own way:
How to implement history.back() in angular.js
But, in case, you want to know how to keep the last state with angular and UI-Router, we can do it with a service. There is some naive implementation tracking just last state (not challenging the history.back())
Check the working example
Service definition:
.factory('PreviousState', ['$rootScope', '$state',
function ($rootScope, $state) {
var lastHref = "/home",
lastStateName = "home",
lastParams = {};
$rootScope.$on("$stateChangeSuccess", function (event, toState, toParams
, fromState, fromParams) {
lastStateName = fromState.name;
lastParams = fromParams;
lastHref = $state.href(lastStateName, lastParams)
})
return {
getLastHref: function (){ return lastHref ; },
goToLastState: function (){ return $state.go(lastStateName, lastParams); },
}
}])
So we just do listen the $stateChangeSuccess and keep the track of last state name and its $stateParams.
We can inject our service to all scopes:
.run(['$rootScope', 'PreviousState',
function ($rootScope, PreviousState) {
$rootScope.PreviousState = PreviousState;
}])
And we can use it as a click or href:
<button ng-click="PreviousState.goToLastState()">go back</button>
<a ng-href="#{{PreviousState.getLastHref()}}" > go to last href</a>
Check that in action here
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.