I have a state that looks like something like this
.state('home.foo.bar', {
url: 'view?one&two',
views: {
'base': {
templateUrl: 'blah',
controller: 'blahCtrl'
}
},
reloadOnSearch: false
})
if I navigate to this state in my template with
ui-sref="home.foo.bar({one: valueOne, two: valueTwo})"
all is well and the url is
.com/view?one=valueOne&two=valueTwo
however if I refresh the page the url changes to just .com/view and neither $state.params or $stateParams contains the values.
if I add a resolve step like so
.state('home.foo.bar', {
url: 'view?one&two',
views: {
'base': {
templateUrl: 'blah',
controller: 'blahCtrl'
}
},
resolve: {
p: function($location, $stateParams, $state) {
console.log($location.search(), $stateParams, $state);
if(!$stateParams.one && !$stateParams.two {
$stateParams.one = $location.search().one;
$stateParams.two = $location.search().two;
} else {
return $location.search().one;
}
}
},
reloadOnSearch: false
})
then when I initially navigation to the state in my template the log statement prints nothing for $location.search() but shows oneValue and twoValue in the $stateParams and $state.params. When I refresh its opposite $location.search() shows the parameters and their values correctly but $stateParams and $state.params are empty.
adding the if statement and setting the values explicitly resolves my bug but this cant be working as intended can it?
am I missing something obvious?
I think it might be because you are missing a slash at the beginning or your URL:
.state('home.foo.bar', {
url: '/view?one&two',
views: {
'base': {
templateUrl: 'blah',
controller: 'blahCtrl'
}
},
reloadOnSearch: false
})
I had the same problem. However it turned out that the problem is not angular. Check your routing on the server, there may change url.
In my case, I interfered the regular statechange/statestart/statesuccess
so in my resolving in statesuccess I have added:
//$state.go(toState,toParams);
//when I manupulated the regular ui-router params
$state.go($rootScope.toState,$rootScope.toParams);
So ve aware of passing toParams when you change the regular ui-router parameters control.
Related
I am using UI-Router to navigate to detailed view. It changes state correctly and passes parameters correctly, yet the url address in the browser stays unchanged. I would like to display the passed params in the url.
app.js
'use strict';
var myApp = angular.module("myApp", ['ui.router']);
myApp.config(['$stateProvider', '$urlRouterProvider','$locationProvider',
function($stateProvider, $urlRouterProvider,$locationProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('/', {
url: '/',
templateUrl: 'partials/listView.html'
})
.state('list', {
url: '/',
templateUrl: 'partials/listView.html'
})
.state('detail', {
url: '/detail/:key',
params: {
key: { value: "" }
},
templateUrl: 'partials/detailView.html',
controller: 'DetailController'
})
// use the HTML5 History API
$locationProvider.html5Mode(true);
}]);
Controller function to go to the detail state:
myApp.controller('MainContr', [ '$scope', '$http', '$location', '$state',
'$filter','$rootScope', MainContr ]);
function MainContr($scope, $http, $location, $state, $filter,$rootScope) {
$scope.goDetailView= function(key){
console.log("trying to change url ");
// changes state correctly, and passes params to DetailController,
// but does not change browser address url.
// How can I update the url in browser here?
$state.go('detail',
{key: $scope.selectedPDFKey},
{
location:true
});
}
}
// detail view
myApp.controller('DetailController', [ '$scope', '$stateParams'
DetailController ]);
function PDFDetailController($scope,$state)
{
$scope.currentKey=$state.params.key;
}
If I remove params in $state.go('detail'), the url in browser address bar is replaced. How can I get url in browser address bar replaced as well when I pass parameters in $state.go(). Thank you.
Issue was fixed when state was changed to use query in url as:
.state('detail', {
url: '/detail?key',
params: {
key: { value: "" }
},
templateUrl: 'partials/detailView.html',
controller: 'DetailController'
})
I came across this, but in my case it was just a missing / at the beginning of the url. So this is wrong:
.state('detail', {
url: 'detail/:key',
templateUrl: 'tpl.html'
});
And this is correct/working:
.state('detail', {
url: '/detail/:key',
templateUrl: 'tpl.html'
});
I dont needed any <base> tags in my <head> at all. My application is in a subfolder called /app.
By default - UI-Router will always show the param in address bar, if is defined in the url (not just in params : {} option)
To prove it, there is a plunker with your scenario, which does what you would expect - http://plnkr.co/edit/9uBlhNoNqZsjAJEdIhYa?p=preview
Links
<a ui-sref="list">
<a ui-sref="detail({key:1})">
<a ui-sref="detail({key:22})">
A state def (as yours)
.state('list', {
url: '/',
templateUrl: 'tpl.html',
controller: 'ParentCtrl',
})
.state('detail', {
url: '/detail/:key',
params: {
key: { value: "" }
},
templateUrl: 'tpl.html',
controller: 'ChildCtrl',
});
Check it here
(you can run it in separate window, by clicking the icon in the top right corner - and see the address bar with a key param)
I am using angular ui for routing in angular js.
I have a url like http://servername/#/news/14.But I want to use aliases and have url like http://servername/#/news/news_title. Is it possible in angular ui??
Here is my config
angular.module('News', ['ui.router'])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('news', {
url: "/news",
templateUrl: "modules/news/views/news.html",
controller: 'NewsCtrl'
})
.state('news.detail', {
url: '^/news/:id',
views: {
'#': {
templateUrl: 'modules/news/views/news_detail.html',
controller: 'NewsDetailCtrl'
}
},
});
})
Of course it is possible. But to make it happen, you need to somehow map the news_title to 14 which i guess is an id. You can write something like:
$stateProvider
.state('news', {
url: "/news/:news_title",
templateUrl: 'news.html',
...
})
in this case, you can type url like http://http://servername/#/news/latest_bbc_news, in your controller you can get the :news_title by using $stateParams
how you gonna map the news_title to 14 depends on your model.
update
For example you probably can write something like this:
$stateProvider
.state('news', {
url: "/news/:news_title",
templateUrl: 'news.html',
resolve : {
news_item: function(NewsService, $stateParams) {
return NewsService.getByTitle($stateParams.news_title);
},
},
controller:['$scope','$state','news_item',
function ( $scope , $state , news_item){
$scope.news_item= news_item;
}]
})
then in your NewsService you need to implement the method getByTitle to either get the news by title from backend directly, or you fetch all news from backend and cache it in a variable like
var news = [
{ID: 1, Title : 'A', Content : 'XXXXXXXXXXXXXXXXXXXX',},
{ID: 2, Title : 'B', Content : 'YYYYYYYYYYYYYYYYYYYY',},
];
and you have to write your own code to loop through it and find the news you need by title.
Again, ui-router is just setting up the routing rules for you, it is always your responsibility to serve the correct content. I mean ui-router have no idea which news have id 14 and the corresponding title, it can only be fetched from backend and processed by you.
What I am trying to do is within the Search controller, once I get the search results back from the server ($http) change view to a different view - the search results view. I am not sure if the approach I am going about it is right, but either-way it doesn't seem to be working. I will need to pass the response as well, so the new view can display the results/response.
My app.js:
.....state('tab.search', {
url: '/search',
views: {
'tab-search': {
templateUrl: 'templates/tab-search.html',
controller: 'SearchCtrl as search'
}
}
})
.state('tab.search-results', {
url: '/results',
views: {
'tab-search-results': {
templateUrl: 'templates/tab-search-results.html',
controller: 'SearchResultsCtrl as searchResults'
}
}
})
Then my search controller has:
.controller('SearchCtrl', function($scope, $state, $location, $ionicPopup, service) {
....
$scope.doSearch = function(state) {
.....
var result = service.doSearch(dataObj);
result.then(function(response) {
console.log("I'm here");
$state.go('tab.search-results');
......
My search results view (tab-search-results.html) has the following basic code at the moment:
<ion-view view-title="Search Results">
<ion-content padding="true">
hello world
</ion-content>
</ion-view>
This basic structure is how all my other pages/views are setup too.
What happens when I perform the search is that the console message gets outputted, and then the URL changes to /results as per the tab.search-results state, but the template/view doesn't change/show.
Interestingly if I change $state.go('tab.search-results'); to point to another app state/view that I know works, it works perfectly - but for whatever reason this state/view isn't working.
Also, if there is a better way of achieving this same thing, then please let me know. I will be needing to eventually pass the "response" from SearchCtrl to SearchResultsCtrl - or rather access it on the search results page in one form or another.
Many thanks.
I think you are looking for $stateParams.
var result = service.doSearch(dataObj);
result.then(function(response) {
$state.go('tab.search-results', {'searchData':response});
}
In your routes file:
.state('tab.search-results', {
url: '/results/:searchData',
views: {
'tab-search-results': {
templateUrl: 'templates/tab-search-results.html',
controller: 'SearchResultsCtrl as searchResults'
}
}
})
And in your SearchResultsCtrl:
.controller($stateParams) {
console.log($stateParams.searchData) // will give you search results
}
NOTE:If you don't want to pass data through the URL you can use params key in the .state() method.
.state('tab.search-results', {
url: '/results',
params: {
'searchData':null
},
views: {
'tab-search-results': {
templateUrl: 'templates/tab-search-results.html',
controller: 'SearchResultsCtrl as searchResults'
}
}
})
I realised why my view wasn't changing properly. The fix was changing the views in the sub-view to reference the parent view.
Fail (sub-view has unique name from parent):
.....state('tab.search', {
url: '/search',
views: {
'tab-search': {
templateUrl: 'templates/tab-search.html',
controller: 'SearchCtrl as search'
}
}
})
.state('tab.search-results', {
url: '/results',
views: {
'tab-search-results': {
templateUrl: 'templates/tab-search-results.html',
controller: 'SearchResultsCtrl as searchResults'
}
}
})
Success (sub-view references parent, 'tab-search'):
.....state('tab.search', {
url: '/search',
views: {
'tab-search': {
templateUrl: 'templates/tab-search.html',
controller: 'SearchCtrl as search'
}
}
})
.state('tab.search-results', {
url: '/results',
views: {
'tab-search': {
templateUrl: 'templates/tab-search-results.html',
controller: 'SearchResultsCtrl as searchResults'
}
}
})
Thanks all, I think I worked out the problem. It was putting the search results page under the tab abstract state. eg: tab.search-results rather than search-results - I am guessing this was the problem as there is no search results tab. When I re-named the state to just search-results (and modified the $state.go to use 'search-results' instead of 'tab.search-results') it worked. Does this seem right?
I have a state that has multiple views declared in it as follows:
$stateProvider
.state('home.details.item', {
url: '^/details',
views: {
'chartsView': {
templateUrl: 'charts.html',
controller: 'chartsCtrl'
},
'gridView': {
templateUrl: 'grid.html',
controller: 'gridCtrl'
},
'detailsView': {
templateUrl: 'details.html',
controller: 'detailsCtrl'
}
}
});
I need to reload one of the views without reloading the whole state, without using $state.go($state.current,null , {reload: true}) , and if possible, from the chartCtrl reload detailsCtrl. Is that possible?
I'd say, that the UI-Router solution should be built arround *states*, not views.
(I created working example here). Other words, if there are
some views which should not be reloaded and
some other views, which should be reloaded
... it calls for state nesting. Let's move that view into child state:
.state('home.details.item', {
url: '^/details',
views: {
'chartsView': {
templateUrl: 'tpl.charts.html',
controller: 'chartsCtrl'
},
'gridView': {
templateUrl: 'tpl.grid.html',
controller: 'gridCtrl'
},
// 'detailsView': {
// templateUrl: 'details.html',
// controller: 'detailsCtrl'
// }
}
})
.state('home.details.item.more', {
views: {
'detailsView#home.details': {
templateUrl: 'tpl.details.html',
controller: 'detailsCtrl'
}
}
})
We also need a state, which will do the reload. We could use other way, e.g. with some changing parameter in state more, but that would mean to change the param value on each call. With this specil state, we can easily reload our state 'more':
.state('reload', {
parent: "home.details.item",
views: {
'detailsView#home.details': {
// this controller will just redirect to 'more' and make it fresh...
controller: ['$state', function($state) { $state.go('^.more')}],
}
}
})
And with these simple controllers we can do all that required stuff:
.controller('chartsCtrl', function ($scope, $state) {
var childName = ".more";
$state.go(childName); // default is a sub state 'more'
})
.controller('detailsCtrl', function ($scope) {
$scope.when = Date.now();
})
Having this: we can call this to reload just details:
<a ui-sref="reload">force reload detail view</a>
Now, when navigating to reload, we will be redirected to state "more" and our view will be rerendered.
SUMMARY:
In general, UI-Router represents state machine. I would strongly suggest:
Do not worry to think in states. Views are just their representation in the DOM.
If there are some features related, they most likely represent state. If others do not relate (should be changed often or rarely) they belong to other state. It could be parent, child or sibling...
Check it here
I have a abstract root state called 'site' with empty url i.e. '' , which has two child state 'profile' and 'home' where the url for home is '/' and the url for profile is '/{username:[a-zA-Z0-9]{3,20}}' inorder to test I tried many times to go to the profile state but it always reaches the home state.
But when i change the home state url with say '/home' then i'm able to go to the profile state. After searching the web i couldn't find this issue. Is it a bug? or i'm doing something wrong?
Here is the code for $stateProvider
$stateProvider.
state('site', {
url: '',
abstract: true,
views: {
'': {
templateUrl: 'views/site.html' //this is the base template
},
'navbar#site': {
templateUrl:'views/navbar.html'
}
},
data:{
access: access.public
}
}).
state('site.home', {
url: '/',
templateProvider :['$rootScope','$http','$state',function($rootScope,$http,$state){
//console.log($state);
var template = 'views/main_new.html';
if($rootScope.User.isLoggedIn()){
template = 'views/new_room_me.html'
}
return $http.get(template).then(function (resp) {
return resp.data;
});
}]
}).
state('site.profile',{
url:'/{username:[a-z0-9A-Z]{3,20},strict:true}',
templateUrl:'views/new_room_friend.html'
});
$urlRouterProvider.otherwise('/ankit');
There is a working example, where I mostly switched the order of state declaration:
.state('site.profile',{
url:'/{username:[a-z0-9A-Z]{3,20}}',
...
})
.state('site.home', {
url: '/',
...
})
The reason is that ui-router url engine does iterate states in order they were declared, and tries to find a url match. this way, we will get the working definition.
But I would suggest:
use some keyword to distinguish url, e.g. url:'/profile/{username:[a-z0-9A-Z]{3,20}}',
Not only this will help the engine to find the proper state by url, but even from perspective of the user/reader of the url - this seems to me more explicit, self-descriptive.
Anyhow, switching state def should solve the issue here...