I have some parameters for category's URL like /:rootcategory/:sub/:sub which is dynamic and for product I have to route like /product/:productname/:producturl..... It is working good using ui-sref but when I come on the URL and refresh it the config got confused and in both URL case it sends me on the category page.
.state('category3',{
url: '/:name1/:name2/:name3',
templateUrl: 'template/product/productsgridpage.html',
controller: 'categoryCtrl',
parent: 'productpagelayout'
})
.state('product', {
url: '/product/:name1/:name2',
templateUrl: 'template/product/detailpage.html',
controller: 'productCtrl',
parent: 'productpagelayout',
})
Updated Answer:
The problem was category also validates product pages, url parameter of category state (/:name1/:name2/:name3) will accept (/product/disc/test) because name1 does not have any special checks to restrict the keyword products from not passing through. So what I suggest is have a regex check which will restrict the keyword product, so that this issue will not occour.
The regex for category will be like
url: '/{name1:\b(?!\bproduct\b)\w+\b}/:name2/:name3',
if name1 parameter starts with product the regex check will fail and category state will not be entered, so it will go to product state, this is what is needed by us.
.state('category3',{
url: '/{name1:\b(?!\bproduct\b)\w+\b}/:name2/:name3',
templateUrl: 'template/product/productsgridpage.html',
controller: 'categoryCtrl',
parent: 'productpagelayout'
})
.state('product', {
url: '/product/:name1/:name2',
templateUrl: 'template/product/detailpage.html',
controller: 'productCtrl',
parent: 'productpagelayout',
})
Previous Answer:
Since you pass "products/hplaptop5050/hp-laptop" it is taking products parameter as a category itself, thus goes to category instead, you need a static variable or even an unique input variable so that it does not get confused, might I suggest something like "/{categoryid:int}/:name1/:name2/:name3" or "/0/:name1/:name2/:name3", so that we have a clear difference from the product and category page.
.state('category3',{
url: '/{categoryid:int}/:name1/:name2/:name3',
templateUrl: 'template/product/productsgridpage.html',
controller: 'categoryCtrl',
parent: 'productpagelayout'
})
.state('product', {
url: '/product/:name1/:name2',
templateUrl: 'template/product/detailpage.html',
controller: 'productCtrl',
parent: 'productpagelayout',
})
Please read the section state declaration page from UI router docs, you can come up with a different approch if needed.
Reference: ui router state docs
Related
we have three pages in our app which can be classified under one parent page as below.
1)Parent
I)Child1
II)Child2
III)ChildIII
.state('Parent', {
url: '/Parent/:ID',
templateUrl: 'parent.html',
controller:'parentcontroller'
})
.state('Parent.Child1', {
url: '/Child1',
templateUrl: 'Child1.html'
})
.state('Parent.Child2', {
url: '/Child2',
templateUrl: 'Child2.html'
})
.state('Parent.Child3', {
url: '/Child3',
templateUrl: 'Child3.html'
})
sometimes we need to call this child pages sequentially one after another from child1 to child3 without parameters and sometimes we need to call those child pages individually but requests needs to go through parent controller so that we do not have to instantiate new controller instance for each one of those child pages. to accomplish this i'm using href but i want to be able to call parent and child with out passing any params.
working href ex: <a href='../Parent/{{ID}}/child1'
Not working ex: <a href='../Parent/child1'
can anyone please guide me to accomplish this?? Thanks!!
solution:
.state('Parent', {
url: '/Parent/:ID',
templateUrl: 'parent.html',
controller:'parentcontroller',
params:{ID:null}
})
.state('Parent.Child1', {
url: '/Child1',
templateUrl: 'Child1.html'
})
.state('Parent.Child2', {
url: '/Child2',
templateUrl: 'Child2.html'
})
.state('Parent.Child3', {
url: '/Child3',
templateUrl: 'Child3.html'
})
From HTML:<a ui-sref="(Parent.Child1{ID:{{value}}})">Home</a>
i wasn't aware that if we use SREF as above and controller declared at parent value can still read the params but gave a shot and it worked!!
Yes, you can do this. First you should define your params in your .state(), if you are passing params to child1 state, then it should defined by like below,
.state('Parent.Child1', {
url: '/Child1',
templateUrl: 'Child1.html',
params: {
paramName: '' //here paramName can be named by your wish.
}
})
after this you should pass the call the state and pass param in ui-sref like below,
<a ui-sref="Parent.Child1({paramName: scopeName})">Click</a>
here scopeName is the name of the scope which have the value to param, if you want hard code a string, then you can pass in qoutes, ({paramName: 'some string'})
I'm a little confused about what you're asking, but the way it sounds is that all the pages use the same controller. If that's the case, just let them. Controllers will eventually be destroyed when they are no longer needed, so instantiating a new one is fine. If you're routing to a new page, it's going to look for the accompanying controller.
--Edit based on OP's comment--
If the goal is to only get data once, you can store that in a service. Every service is a singleton, so if you grab data and save it in your service, you can inject that service elsewhere and have access to it.
I have app with many main states, one of them is user profile:
$stateProvider.state('profile', {
url: '/profile/',
templateUrl: 'profile/profile.html',
controller: 'Profile',
});
But this is just an container for nested pages with different profile settings. It's template only contains main menu and ui-view for nested states. Controller is only for that menu handling.
One of nested views should be default url and have same URL as parent, so there shouldn't be any suffixes added into url, but I can't achieve that.
Here's what I tried:
$stateProvider.state('profile.details', {
url: '',
templateUrl: 'profile/details.html',
controller: 'ProfileDetails',
});
this is not working at all, at url /profile/ only menu appears and an empty ui-view element. Second approach:
$stateProvider.state('profile.details', {
url: '/',
templateUrl: 'profile/details.html',
controller: 'ProfileDetails',
});
This matches on url /profile// (with 2 slashes at end). At url /profile/ there is still menu and empty ui-view element.
How can I achieve that result? Is this even possible using angular-ui-router?
Make your parent state abstract. This will prevent from going into that state, and force to go to child states only. Abstract states are perfect as templates for child ones. Also get rid of url:
$stateProvider.state('profile', {
abstract: true,
templateUrl: 'profile/profile.html',
controller: 'Profile',
});
Now for your child state define absolute URL
$stateProvider.state('profile.details', {
url: '^profile',
templateUrl: 'profile/details.html',
controller: 'ProfileDetails',
});
That should work.
I am using Angular UI Router , and I have setup two routes
One for all the content pages like /about, /terms etc
$stateProvider.state('sidebarPages.page', {
url: ':slug',
views : {
...
}
});
And now I want to add another for other pages like our-team
$stateProvider.state('sidebarPages.page', {
url: 'our-team',
views : {
...
}
});
The problem is that the second state is ignored when I go to page /our-team and the first one is executed instead which is :slug , and could accept everything.
Is there a way that I can create these two states, one for specific pages , and one that will accept everything and put it in slug param , and based on param I can then bring it from DB.
I created working plunker here. The order decides. Create states with known names, then the one with the slug:
// States
$stateProvider
.state('home', {
url: "/home",
templateUrl: 'tpl.html',
})
.state('other', {
url: "/other",
templateUrl: 'tpl.html',
})
.state('slug', {
url: "/:slug",
templateUrl: 'tpl.html',
})
;
Check it here
I started building ionic app on top of the sidemenu starter app. The starter app has a base state 'app' which is abstract and all the sidemenu pages are children of the app for example app.search, app.browse, app.playlists etc.
I have similar hierarchy. However, I want the start page to be some other page, which means it is at the app level.
The states look like this:
$stateProvider
.state('app', {
url: "/app",
abstract: true,
templateUrl: "templates/menu.html",
controller: 'AppCtrl'
})
.state('join', {
url: "/join",
views: {
'menuContent' :{
templateUrl: "templates/join.html",
controller: 'joinCtrl'
}
}
})
.state('app.search', {
url: "/search",
views: {
'menuContent' :{
templateUrl: "templates/search.html",
controller: 'searchCtrl'
}
}
})
.state('app.results', {
url: "/results",
views: {
'menuContent' :{
templateUrl: "templates/results.html",
controller: 'resultsCtrl'
}
}
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/join');
When I run the app, the url defaults to
http://192.168.1.4:8100/#/join
and shows a blank page. Obviously, the join.html is not blank. Also, the console.log messages in joinCtrl are not outputted.
I am not able to figure out why is it not loading the join page. When I change the otherwise to point to '/app/search', everything works.
Any idea what's going on? How do I load the initial page by default and then navigate to the 'app.search' state?
I would expect that because the app is abstract - it is there for a reason. To be parent/layout state. In its template should most likely live all other states.
If yes - check this working example I created to demonstrate that. What we need is to mark the join as a child of the app state. Then the 'menuContent' placeholder will be properly searched in the app template:
.state('join', {
parent: 'app',
url: "^/join",
views: {
'menuContent': {
templateUrl: "tpl.join.html",
controller: 'joinCtrl'
}
}
})
There is a working plunker
The definition url: "^/join", is there to support the idea, that the url defined like this:
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/join');
will work even for nested state (join is child of app). See:
Absolute Routes (^)
If you want to have absolute url matching, then you need to prefix your url string with a special symbol '^'.
This is just one way... we can do the similar stuff if the join is not nested state, but then it should target the unnmaed view '' instead of 'menuContent'
Consider the following ui-router configuration:
$stateProvider
.state('app', {
url: "/app",
abstract: true,
templateUrl: "templates/menu.html",
controller: 'AppCtrl'
})
.state('app.feed', {
url: "/feed",
views: {
'menuContent': {
templateUrl: "templates/feed.html",
controller: "FeedCtrl"
}
}
})
I have the following link in my app:
<a ui-sref="app.feed({ feedId: 5 })">My Link</a>
I'm trying to get the feedId in state params:
angular.module('MyApp').controller('FeedCtrl', function($state) {
console.log($state.params);
});
But, unfortunately, $state.params is always {}.
What am I doing wrong?
BONUS QUESTION
I expect FeedCtrl to be initialised every time I click on the link, i.e. every time the state is changed to app.feed. So, If I change between app.feed state and some other state multiple times, I expect the console.log above to run multiple times. But, it looks like ui-router initialises FeedCtrl only once. Why?
To pass a parameter - we have to define it:
.state('app.feed', {
url: "/feed/{feedId}",
views: {
'menuContent': {
templateUrl: "templates/feed.html",
controller: "FeedCtrl"
}
}
In this case we define it as {feedId}
See more here
URL Parameters
Some examples:
'/user/{id:[^/]*}' - Same as '/user/{id}' from the previous example.
'/user/{id:[0-9a-fA-F]{1,8}}' - Similar to the previous example, but only matches if the id parameter consists of 1 to 8 hex digits.
'/files/{path:.*}' - Matches any URL starting with '/files/' and captures the rest of the path into the parameter 'path'.
'/files/*path' - Ditto. Special syntax for catch all.
In case we do have parameter defined, and we will navigate among states with different feedId values... we will really see, that that controller FeedCtrl is reinstantiated. That's how the ui-router was designed