The multiple nested views functionality of the ui-router is very nice - you can easily jump from one state of your app to another.
Sometimes you might want to change the URL, but sometimes not. I feel like the concept of state should be separate/optional from routing.
Here's a plunker that shows what I mean. This is a fork of one of the plunkers in the ui-router documentation, with 2 minor changes noted below:
.state('route1', {
url: "/route", // <---- URL IS SHARED WITH ROUTE2
views: {
"viewA": {
template: "route1.viewA"
},
"viewB": {
template: "route1.viewB"
}
}
})
.state('route2', {
url: "/route", // <---- URL IS SHARED WITH ROUTE1
views: {
"viewA": {
template: "route2.viewA"
},
"viewB": {
template: "route2.viewB"
}
}
})
This seems to work - the URL stays the same. Again, how much redundant work is done here? Is this an approved/tested usage?
It would be nice if you could omit the url from a state..
UPDATE: You can omit a url from a state. plunker
Update question: Is this an approved/tested usage?
You can absolutely have a state without a URL. In fact, none of your states need URLs. That's a core part of the design. Having said that, I wouldn't do what you did above.
If you want two states to have the same URL, create an abstract parent state, assign a URL to it, and make the two states children of it (with no URL for either one).
To add to the other answer, Multiple Named Views do not use a URL.
From the docs:
If you define a views object, your state's templateUrl, template and
templateProvider will be ignored. So in the case that you need a
parent layout of these views, you can define an abstract state that
contains a template, and a child state under the layout state that
contains the 'views' object.
The reason for using named views is so that you can have more than one ui-view per template or in other words multiple views inside a single state. This way,
you can change the parts of your site using your routing even if the URL does not change and you can also reuse data in different templates because it's a
component with it's own controller and view.
See Angular Routing using ui-router for an in-depth explanation with examples.
Related
I have a ui-view,
<div ui-view="filtersView_ModalA" class="filter-container"></div>
Now, I want to make generic routes, so that going forward, if any new filterView needs to be implemented like say,
<div ui-view="filtersView_ModalB" class="filter-container"></div>
My route can handle the same.
I am getting ModalA or ModalB from stateParams.prodType.
.state('Modal.tabs', {
url: .......,
views: {
'filtersView_{{stateParams.prodType}}#Modal.tabs': {
templateUrl: function(stateParams) {
// stateParams.prodType works here
.....
},
It's not working.
I also tried , 'filtersView_' + stateParams.prodType + '#Modal.tabs' : {
Nothing worked.
Or, can I declare a constant and concat the values in view names?
Am I doing something wrong?
No, you can't compose the view-name.
But you can do that using named views in different states. You can specify which component should be rendered in the view in which state. See more in the documentation: https://github.com/angular-ui/ui-router/wiki/Nested-States-and-Nested-Views
And also this entry: https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
I have a case using Ember where I want to make the top level URL available (ie. localhost:4200/demo), and have all the routes underneath also display the same URL (localhost:4200/demo). So the route file, if possible would look something like:
this.route('demo', function() {
this.route('one', { path: '/' });
this.route('submit', { path: '/' });
});
I understand that ENV.locationtype can be set for the whole app, but is there a way to conditionally set this for specific URLs underneath a parent URL?
Generally when you end up hitting major snags like this it is because Ember is implicitly trying to tell you that what you are doing isn't a good idea.
Is there a particular reason that you don't want your sub-routes to affect the URL in any way? Could you get by with random values in the URL if your prime purpose is to obfuscate things?
Ember uses the URL to work out what state things should be in in your app. If you don't want to use the routes at all you wouldn't have to, but then at that point you are dealing with a nested hierarchy of components that you have to switch between yourself. Which would in essence be akin to using React without a router ...
By default, Ember can manage URLs, or it can be set to not manage them, but it seems like the desired intent is to have it do both in a single environment, which is not logically allowed.
If none is declared for ENV.locationtype, then Ember's default URL management is turned off. This is an "environment-wide" configuration.
If the Ember Router is being used to map nested routes, and default URL management is in play, then observe that you cannot have the same URL path defined for multiple, sibling, child routes.
A further observation, is that your attempt above is tapping into functionality governed by the single index route that is available at every nesting level within the Router map. However, a route cannot have multiple index routes. Only the last one defined will be recognized.
Router.map(function() {
this.route('demo', function() {
this.route('one', { path: '/' }) // <-- this is over-ridden by "submit"
this.route('submit', { path: '/' }) // <-- this defines an "index" route for demo
})
})
I have define state for my shop application but I'm not sure I'm doing it right. Since I have more than one optional parameter in url I'm not sure how should I implement it.
.state('app.products', {
abstract: true,
views: {
'content#': {
templateUrl: 'app/products/views/product.html'
},
'productHeader#app.products': {
templateUrl: 'app/products/views/product-header.html'
}
}
})
Above is my abstract view for products page. Products will be separated in man/women and also subcategories like:
www.example.com/man/
www.example.com/man/footwear/
www.example.com/man/footwear/shoes
Man, footwear and shoes are all optional since man param can be woman, footwear can be cloth (where last param would be e.g. shirts) and all possible combinations of those above.
I'm not sure if I have to make every state separately or I can handle all this with one more state except this one?
Just to note, product header is not relevant here and if its required for good structure to remove it, surely I can do that.
I just can't find anything similar online, so link would be also be helpful if anyone has any.
I've done something very similar recently by nesting each subcategory state into its parent category state. Some benefits of doing it this way are that you save yourself from having to repeat a lot of code in a child state that was already defined in a parent state, and also from having to reload data and views that were already loaded in the parent state.
Here's an example to get you started:
.state('app.products', {
abstract: true,
url: '/products',
views: {...}
})
.state('app.products.gender', {
url: '/:gender',
views: {...}
})
.state('app.products.gender.category', {
url: '/:category',
views: {...}
})
.state('app.products.gender.category.type', {
url: '/:type',
views: {...}
})
First, the urls automatically stack in child states. This means you only have to define one url parameter per child state and you still wind up getting urls like this /app/products/:gender/:category/:type.
The second benefit of doing it this way is that views defined in a parent state are automatically included in all of its child states, unless you explicitly override it:
.state('app.products.gender.category', {
url: '/:category',
views: {
'foo#someParentState': {templateUrl: 'foo.html'},
'bar#someParentState': {templateUrl: 'bar.html'}
}
})
.state('app.products.gender.category.type', {
url: '/:type',
views: {
// foo.html is still rendered here
// bar.html is replaced by baz.html
'bar#someParentState': {templateUrl: 'baz.html'}
}
})
Another benefit seen from this example, is that foo.html won't be reloaded when the state changes to app.products.gender.category.type. For example, say foo.html has a long scrolling list of types in that category. If the user clicks on an item in the list that changes the state from app.products.gender.category to the child state app.products.gender.category.type, then foo's long scrolling list will not be reloaded, and the user can still see the item they clicked on. On the other hand, if that click had changed the state to a non-child state, then the list would probably have been reloaded (data and all), and the user might have to scroll to see the item they clicked on.
Some advice:
Keep your nested state names short.
Only include a state in the hierarchy if it's absolutely necessary (I'm looking at you app.products!).
There are plenty of ways that this technique can go wrong so be sure to review the ui-router docs for configurations that help you code less.
I have an AngularJS site, the object-resource I want to show is:
each user has a basic account, that will show in a single page (named basic-page);
user has several sub-account, each sub-account will show in a diffent page (named app-page);
basic-page will show the summer info about the sub-account, so app-page can share the loaded $http data of basic-page is better for code reusing.
As the purpose, I use ui-router define state below:
.state('user', {
url: '/user/{id}',
title: 'User-Page',
templateUrl: helper.basepath('user.html')
})
.state('user.app', {
url: '/{app}',
title: 'App-Page',
emplateUrl: helper.basepath('app.html')
})
Notice that state user.app is the child of user.
What I want is when I enter the user.app, it can reuse the data in user, ecen if it's a different page, that the user need not to contain a ui-view to include user.app's template.
But actually I enter user.app, and it doesn't show the app.html(because I didn't include ui-view in user.html).
Maybe this is not the correct usage of ui-router.
So, how can I share data in different $state? Anyone can give me a detailed example? Thank you.
Sharing data across controllers
Any time you need to share data across states you will need to create a service/factory that you can instantiate in your controllers associated with those states.
The factory will consist of basic getters and setter for the different data you need to share. Just like when you build getters and setters in java to share across classes.
Example Code
.factory('yourFactory', function ($scope) {
return {
get: function () {
return $scope.someValue;
},
set: function(value){
$scope.someValue = value;
}
};
})
Disclaimer: I've not tested this code but it should do the job for getting and setting some values you need to access across your app.
Demo : Working plunker with this approach.
Alternative: 1
This is the "Dirty" alternative, you can set a global variable with $rootScope. It will be accessible everywhere since its global, I strongly advise you don't do this but though I would point it out to you anyway.
Alternative: 2
When a state is "active"—all of its ancestor states are implicitly active as well.So you can build your states considering the parent-child relationship and share data across scopes in hierarchical manner.
Official Docs and working plunker with mentioned approach.
I have an app that is currently built to have a static base URL with a parameter at the end. I would like to instead have the base URL default to one vaule, but have the ability to built routes based on several options. So for now its set up as:
.state('ball', {
parent: 'ballLayout',
url: '/ball/{urlName}',
views: {
'cube.head': {
templateUrl: 'partials/views/ball.head.html',
controller: 'BallCtrl'
}
}
});
The static ball value is what I'd like to change. Basically I'd like to have an optional list of incoming URLs that would work, but when nothing is present it defaults to ball. So for instance:
ball/title-of-page
bat/title-of-page
basket/title-of-page
beast/title-of-page
These would all work, and when constructing the URL it would default to ball/
Is something like this possible? How would one go about implementation.
I dont think what I'm asking here can actually be done without having issues with other parameters. Instead Im asking a new question about Regex from incoming links to reroute to my angular URL.