I'm currently working on a small application that shows a 'drill down' type of menu. Unlike a TreeView my component only displays the current level.
There are only 3 levels: main, submenu, item. Drilling deeper (on the item) will result in a new state that corresponds to a detail view of the selected item.
I have only 2 states, the first is valid for any of the three levels. The second is the detail state:
$stateProvider.state('menu', {
url: '/menu/:submenu/:item',
templateUrl: 'src/menu/views/menu.html',
controller: 'MenuController',
controllerAs: 'MenuController',
resolve: {
data: function($stateParams, MenuService){
return MenuService.loadUri($stateParams);
}
}
}).state('menu.details', {
url: '/:selectedItem',
templateUrl: 'src/menu/views/menu-details.html',
controller: 'MenuDetailsController',
controllerAs: 'MenuDetailsController',
resolve: {
data: function($stateParams, MenuService){
return MenuService.loadUri($stateParams);
}
}
});
This solutions works correctly, however I'm a bit annoyed by the fact I need to define the resolvemethod twice. I am required to do this as $stateParams contains only the params of that exact state (excluding child states) and I need all parameters to form a valid URL to fetch the corresponding resource via the MenuService.
I know that state.params can be used to get all state params, however it doesn't seem to work in the resolve method: it contains the parameters of the previous state.
Does anybody know how I could fix this? Having a child state that should simply trigger a new view, whilst using the resolve method of the parent state with all state parameters?
If you think I'm completely misunderstanding how states work, then please go ahead and tell me :)
Related
What is the design pattern for next problem?
I have a state 'a.list'. It shows list data and filter. When i change the filter it reloads the 'a.list' state with new search parameters (?type=emp&ageFrom=3...)
I want to have a child state to show details of selected item 'a.list.detail'.
I'm wondering how to do this in best way because i want to keep my filter parameters when i back to list view from detail.
You can create a state for details with parameters and you can access it in this way
state('yiur new state', {
url: '/new',
params: {
type: null,
ageFrom:null
},
templateUrl: 'new.html',
controller: function($scope, $stateParams) {
$scope.type= $stateParams.type;
$scope.ageFrom= $stateParams.ageFrom;
}
});
When you get back to back to list view you can be able to get back the details
I have an Angular app which has several dynamic fields, each of these fields are changed updated based on config which comes from a backend database.
In order to control what config is used I need to dynamically switch a single variable - I've decided that the URL is the best way to set/switch the variable as there need to be multiple permutations of the site based on the URL so:-
/:dynamicVariable/
I'm looking for some guidance as to whether this is the best way to do it and what the best way to do it would be? I'm struggling as I don't want to have to set each route for each section like this /:dynamicVariable/homepage /:dynamicVariable/about-us etc etc. Ideally the core module checks it and sets it but the routing ignores it so /:dynamicVariable/ becomes the root.
Hope that makes sense, thanks in advance for your help.
I ended up doing this by using ui.router and nesting states within an abstract parent state which held the client, like so :-
.state('rootClient', {
abstract: true,
url: '/:client',
templateUrl: 'app/layout/layout.html',
controller: 'Layout',
controllerAs: 'layout',
noClient: false,
resolve: {
client: function ($stateParams, ClientService) {
return ClientService.getClient($stateParams.client);
}
}
})
.state('rootClient.home', {
url: '/homepage',
views: {
'content': {
templateUrl: 'app/homepage/homepage.html',
controller: 'Homepage',
controllerAs: 'home'
}
}
});
This way all the routes are under the parent route, I also added a resolve to make sure the client exists before moving to the route. Hopefully this will help someone else down the line.
Cheers
I am currently using the ui-router-tabs (https://github.com/rpocklin/ui-router-tabs) module in my application.
I have got most of it working as I need but I want to select a default tab.
Here is an example of how my states are currently configured (pretty much the way the example is from the site above!):
.state('state', {
url: "/foo/:id/bar",
controller: 'SomeController',
templateUrl: 'pageA.html'
}).state('state.substateA', {
url: "/somepath",
templateUrl: 'partials/pageB.html',
controller: 'SomeOtherController'
}).state('state.substateB', {
url: "/someotherpath",
templateUrl: 'partials/pageC.html',
controller: 'AnotherController'
});
So at present if the url is "/foo/:id/bar" I want to select my first tab by default, is this something I ought to be doing at the controller level, so sniff the current matched state and if equal to "/foo/:id/bar" then trigger another state change manually?
EDIT
OK, so as I was writing the question I came up with a way, but not sure of it's really the angular way of doing it.
So in my controller code I check the current state and set a default sub state accordingly to end up selecting one of my tabs:
if ($state.current.name === 'state') {
$state.go('state.substateA');
}
Does this seem like a sensible approach?
I have an angular app with a homepage that shows a list of things. Each thing has a type. In the nav, there are selectors corresponding to each thing type. Clicking one of these selectors causes the home controller to filter the things shown to those of the selected type. As such, I see the selectors as corresponding to states of the home page.
Now, I'd like to map each of these states to a url route: myapp.com/home loads the home page in default (unfilitered) state, myapp.com/home/foo opens the home page with the foo-type selector activated, and switching from there to myapp.com/home/bar switches to the bar-filtered state without reloading the page.
It's that last bit - triggering "state" changes without reloading the page, that's been particularly tricky to figure out. There are numerous SO/forum questions on this topic but none have quite hit the spot, so I'm wondering if I'm thinking about this in the wrong way: Should I be thinking of these "states" as states at all? Is there a simpler approach?
Also, I'm open to using either ngRoute or ui.router - is there anything about one or the other that might make it simpler to implement this?
Using ui-router, you can approach it like this:
$stateProvider
.state('home', {
url: "/home",
controller: "HomeController",
templateUrl: "home.html"
// .. other options if required
})
.state('home.filtered', {
url: "/{filter}",
controller: "HomeController",
templateUrl: "home.html"
// .. other options if required
})
This creates a filtered state as a child of the home state and means that you can think of the URL to the filtered state as /home/{filter}. Where filter is a state parameter that can then be accessed using $stateParams.
Since you don't want to switch views, you inject $stateParams into your controller, watch $stateParams.filter, and react to it how you wish.
$scope.$watch(function () { return $stateParams.filter }, function (newVal, oldVal) {
// handle it
});
I have three route states - an abstract state, a state that shows the listing and a state that specifically shows a detail of one particular object.
My states are configured as follows:
.state('movies', {
url: "/movies",
template: '<ui-view ng-show="isLoaded" />',
abstract: true,
ncyBreadcrumb: {
skip: true
}
})
.state('movies.index', {
url: "?genres&tags&year&season&page&perpage",
templateUrl: "views/movies.html",
controller: 'MoviesViewController',
ncyBreadcrumb: {
label: 'Movies'
}
})
.state('movies.show', {
url: "/:id/:slug",
controller: 'MoviesItemController',
templateUrl: "views/movies/details.html",
ncyBreadcrumb: {
parent: 'movies.index',
label: '{{item.title_main}}'
}
})
I'd like to add the posibility of movies.index also beginning with a slash, so the URL can either be /movies or /movies/. However with my design, I have no idea how to do that.
I'm also not sure if I understand the concept behind abstract states correctly. I can either use this, or have movies.index be the parent state, in which case, how do I replace its content by the child's template? Also, I have noticed that since the parent controller also gets called, an unnecessary GET request is sent to my API, which can slow things down (especially since it's completely useless information). Can anyone give me a hand in this?
I understand that this use case is supported by $urlMatcherFactory.strictMode(false)
Take a look her
http://angular-ui.github.io/ui-router/site/#/api/ui.router.util.$urlMatcherFactory#methods_strictmode