I am trying to get the query parameters from my angular UI router application. However I cannot seem to get them when I use the query method. The state params are always coming through as undefined.
$stateProvider.state("message",
{
views: {
"main-view": {
templateUrl: "admin/templates/message.html",
controller: ["$scope", "$stateParams", function ($scope, $stateParams )
{
// Get the params
$scope.message = $stateParams.message; // undefined
$scope.error = $stateParams.status; // undefined
}]
}
},
url: "/admin/message?message&status"
})
}
The URL i am using to access this state is:
http://localhost:8080/admin/message?message=hello&status=error
However if I use the Basic Parameters method it works fine. I.e:
url: "/admin/message/:message/:status"
...
http://localhost:8080/admin/message/hello/error
I have html5 mode activated (not sure if that affects it?)
My code has been simplified for the above - am I doing something wrong here? Have I forgot to include anything?
Thanks
Related
I've got a ui-router application with a large number of states, and I need to be able to pass a model through $stateParams. On every $http request, I check the response for a "STATE" parameter, which is returned from the server. If it exists, I execute $state.go(STATE).
So effectively, I've got my $stateProvider:
$stateProvider
.state('Account', {url: '/Account', template: '<ui-view/>'})
.state('Account.name', {
url: '/Name',
templateUrl: 'app/Account/partials/Name.html',
controller: 'AccountNameController as nameVm'
})
And many more states that look just like this.
I have a data model that is just a factory with an object that is get and set via functions. So whenever I call saveAccount(), it takes the model and sends it to a Web API backend. The backend verifies the data and sends it back with a STATE parameter (either account.invalid, account.valid, or account.needsMoreInfo). Here's my $httpInterceptor
.factory('httpInterceptor', ['$q', '$injector',
function ($q,$injector) {
return {
'response': function(response) {
if(response.data.state){
$injector.get('$state').go(response.data.state, response.data.account);
}
return response;
}
};
}
])
As you can see, I'm trying to send the account through the stateparams.
In the controller, I basically need to be able to say vm.account = $stateParams.account
My question is:
How can I modify my $states to both have a named controller and also take a state parameter and access that from the controller?
The reason I'm not passing the data through a service is because there are multiple models, so I can't just provide the name of the service in the $httpInterceptor because it isn't constant.
EDIT: Figured it out
Here's what my controller needed to have in it:
if ($stateParams && $stateParams.data){
vm.Account = $stateParams.data;
}
And here's what the state ended up looking like:
.state('taxAccount.invalid', {
url: '/Invalid?params',
templateUrl: 'app/TaxAccount/partials/Invalid.html',
controller: 'taxAccountInvalidController as invalidVm',
params:{data:null}
})
I didn't realize I could put params:{data:null}. I thought the stateParams had to go in the controller declaration in the state configuration.
Here's what my controller needed to have in it:
if ($stateParams && $stateParams.data){
vm.Account = $stateParams.data;
}
And here's what the state ended up looking like:
.state('taxAccount.invalid', {
url: '/Invalid?params',
templateUrl: 'app/TaxAccount/partials/Invalid.html',
controller: 'taxAccountInvalidController as invalidVm',
params:{data:null}
})
I didn't realize I could put params:{data:null}. I thought the stateParams had to go in the controller declaration in the state configuration.
This question is might be similar to this but with different requirements. As I was not able to make comment (required 50 points) I am replicating the question.
I want to simply access the parameters sent from ui-sref in template inside the controller without mentioning them in state URL .
Something like using the link below for transitioning the state to home with foo and bar parameters:
<a ui-sref="home({foo: 'fooVal', bar: 'barVal'})">Go to home state with foo and bar parameters </a>
Receiving foo and bar values in a controller:
state('home', {
url: '/:foo',
views: {
'***whatIsThis***': {
templateUrl: 'home.html',
controller: 'MainRootCtrl'
},
app.controller('SomeController', function($scope, $stateParam) {
//..
var foo = $stateParam.foo; //getting fooVal
var bar = $stateParam.bar; //getting barVal
//..
});
I get undefined for $stateParam in the controller.
Could somebody help me understand how to get it done? I want to get bar as well without adding it to URL
If some of your params are not supposed to show up in the URL, you need to combine url and params:
.state('home', {
url: '/:foo',
params: {
foo: 'default value of foo',
bar: 'default value of bar'
},
...
})
and then use $stateParams properly:
.controller('SomeController', ['$scope', '$stateParams', function ($scope, $stateParams) {
// ... (use $stateParams)
}])
(see documentation).
And, if you're still stuck, please take a look at this working codepen demo.
What's the correct way to update a ui-router view when state parameters change?
For example, if I've got a state like:
.state("page.view", {
url: "/pages/:slug",
views: {
"": {
controller: "PageCtrl",
templateUrl: "page-view.html",
},
},
})
And an (incorrect) controller which looks like this:
.controller("PageCtrl", function($scope, $state) {
$scope.page = loadPageFromSlug($state.params.slug);
})
How can I correctly load a new $scope.page when the $state.slug changes?
Note that the above does not work when moving from page to another because the controller is only run once, when the first page loads.
I would do something like this:
.controller("PageCtrl", function($scope, $state) {
$scope.$on("$stateChangeSuccess", function updatePage() {
$scope.page = $state.params.slug;
});
});
I'd be curious if you find a better way - there may be some way to just watch the value of the state slug, but this is clean and clearly articulates what it is that you're watching for.
I am really not fully sure, if I do not miss something here - but, based on the snippets shown in your question:
PageCtrl is related to state "page.view" and will be run as many times as "page.view" state is triggered
"page.view" state has declared param slug - url: "/pages/:slug",, which will trigger state change - whenever it is changed
If the above is true (if I do not oversee something) we can use stateConfig setting - resolve
there is no need to use $state.params. We can use $stateParams (more UI-Router way I'd personally say)
Well if all that is correct, as shown in this working plunker, we can do it like this
resolver:
var slugResolver = ['$stateParams', '$http'
, function resolveSlug($stateParams, $http){
return $http
.get("slugs.json")
.then(function(response){
var index = $stateParams.slug;
return response.data[index];
});
}];
Adjusted state def:
.state("page.view", {
url: "/pages/:slug",
views: {
"": {
controller: "PageCtrl",
templateUrl: "page-view.html",
resolve: { slug : slugResolver },
},
},
})
And the PageCtrl:
.controller('PageCtrl', function($scope,slug) {
$scope.slug = slug;
})
Check it all in action here
I had this problem in ui-router 0.2.14. After upgrading to 0.2.18 a parameter change does fire the expected $stateChange* events.
I'm in the process of learning AngularJS, working on a more in-depth ToDo app. I'm having an issue with trying to limit access to a url or "route" using angular.
When you hit my dev url on my machine (todo.ang) it brings you to todo.ang/#/home, on this view you see the categories which have todos associated to each. EG (category = cat, cat has a todo of "feed", and "play"), when you click a category I'm calling the $scope.goToCategory function (seen in my JS fiddle) which sets a variable for my firebase ref then redirects you too /#/todo. This is working correctly.
My problem is, I don't want the user to be able to access /#/todo if the todoRef variable is still undefined. But it seems like even after $scope.goToCategory is called and todoRef is set to a firebase URL, the routerprovider never gets recalled to know that todoRef has been set to a different value so it always forces you back to /#/home.
code:
var todoRef = undefined;
if (todoRef !== undefined) {
$routeProvider.when('/todo', {
templateUrl: 'views/todo.html',
controller: 'TodoCtrl'
});
}
$scope.goToCategory = function(catId) {
test = catId;
todoRef = new Firebase("URL HERE");
$location.path('/todo');
}
I didn't include the entire file of code but if thats necessary, I can do that as well.
JSFiddle
All routes are only being set during the config phase.
what happens in your code is that 'todo' route is ignored during the initiation of ngRoute.
What you should do is to setup the route but have a resolve like so:
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/todo', {
templateUrl: 'views/todo.html',
controller: 'TodoCtrl',
resolve: {
todoRef: ['$q', function($q) {
return todoRef ? todoRef : $q.reject('no ref');
}]
}
});
}]);
If 'todoRef' is undefined the route is rejected.
Also you should consider moving 'todoRef' into a service and not on global scope.
You can also listen for route errors and for example redirect to home route:
app.run(['$rootScope', '$location', function($rootScope, $location) {
$rootScope.$on('$routeChangeError', function() {
$location.path('/home');
});
}]);
I'm getting killed by my inability to grok Angular-UI UI-Router. I have a state defined as follows:
$stateProvider
.state('member', {
url: '/member/:membersId',
templateUrl: 'templates/member.html',
resolve : {
// From examples for testing
simpleObj: function(){
return {value: 'simple!'};
},
memberDetails : function(FamilyService,$state) {
return FamilyService.getMember($state.current.params.membersId);
}
},
controller: 'MemberController'
});
Since the docs say $stateParams aren't available, I'm using $state.current.params. Should be fine. Instead, I get dead air. I can't access the membersId to save my life.
To test my service and make sure it's not the problem, I hard coded in a membersId and the controller gets the memberDetails result as well as the simpleObj result. So, Service and Controller and working great.
Example of that hardcoded:
$stateProvider
.state('member', {
url: '/member/:membersId',
templateUrl: 'templates/member.html',
resolve : {
// From examples for testing
simpleObj: function(){
return {value: 'simple!'};
},
memberDetails : function(FamilyService,$state) {
return FamilyService.getMember('52d1ebb1de46c3f103000002');
}
},
controller: 'MemberController'
});
Even though the docs say you can't use $stateParams in a resolve, I've tried it anyway. It doesn't work either.
return FamilyService.getMember($stateParams.membersId);
How in the world do I get the membersId param to get passed into the FamilyService for the resolve?
I don't have much hair left to pull out; so, someone save me please.
I finally figured this out. It was quite simple and in the docs. Despite reading several times, I overlooked it each time. I needed to inject $stateParams into the resolve:
$stateProvider
.state('member', {
url: '/member/:membersId',
templateUrl: 'templates/member.html',
resolve : {
simpleObj: function(){
return {value: 'simple!'};
},
memberDetails : function(FamilyService,$stateParams) {
return FamilyService.getMember($stateParams.membersId);
}
},
controller: 'MemberController'
});
I still don't understand why the documentation says this is not possible.
Two Important $stateParams Gotchas
The $stateParams object is only populated after the state is activated
and all dependencies are resolved. This means you won't have access to
it in state resolve functions. Use $state.current.params instead.
$stateProvider.state('contacts.detail', { resolve: {
someResolve: function($state){
//* We cannot use $stateParams here, the service is not ready //
// Use $state.current.params instead *//
return $state.current.params.contactId + "!"
}; }, // ... })