Trying to migrate an angularjs application to use the new version of angular-ui-router
1.0.14 and stumbled upon a problem when trying to change $stateParams
in the resolve of a state.
For example, previously (when using angular-ui-router
0.3.2) modifying $stateParams
worked like this:
$stateProvider.state('myState', { parent: 'baseState', url: '/calendar?firstAvailableDate', template: 'calendar.html', controller: 'CalendarController', controllerAs: 'calendarCtrl', resolve: { availableDates: ['CalendarService', '$stateParams', function(CalendarService, $stateParams) { return CalendarService.getAvailableDates().then(function(response){ $stateParams.firstAvailableDate = response[0]; return response; }); }] } })
The problem is firstAvailableDate
is populated after a resolve and I do not know how to update $transition$.params()
during a resolve when usign the new version of angular-ui-router
1.0.14.
I have tried, and managed to update the url parameter with
firing a
$state.go('myState', {firstAvailableDate : response[0]})
but this reloads the state, so the screen flickersmodified
$transition$.treeChanges().to[$transition$.treeChanges().length-1].paramValues.firstAvailableDate = response[0];
to actually override the parameters. I have done this after looking through the implementation onparams()
for$transition$
.
Although both those options work, they seem to be hacks rather than by the book implementations.
What is the correct approach to use when trying to modify parameters inside a resolve?
1 Answers
Answers 1
Approach with dynamic parameter:
Take a look at this document: params.paramdeclaration#dynamic. Maybe thats what you are looking for: ...a transition still occurs...
.
When dynamic is true, changes to the parameter value will not cause the state to be entered/exited. The resolves will not be re-fetched, nor will views be reloaded.
Normally, if a parameter value changes, the state which declared that the parameter will be reloaded (entered/exited). When a parameter is dynamic, a transition still occurs, but it does not cause the state to exit/enter.
This can be useful to build UI where the component updates itself when the param values change. A common scenario where this is useful is searching/paging/sorting.
Note that you are not be able to put such logic into your resolve inside your $stateProvider.state
. I would do this by using dynamic
parameters to prevent the state reload. Unfortunally, the dynamic
rules doesn't work when you try to update your state (e.g. by using $stage.go()
) inside the resolve part. So I moved that logic into the controller to make it work nice - DEMO PLNKR.
Since userId
is a dynamic param the view does not get entered/exited again when it was changed.
Define your dynamic param:
$stateProvider.state('userlist.detail', { url: '/:userId', controller: 'userDetail', controllerAs: '$ctrl', params: { userId: { value: '', dynamic: true } }, template: ` <h3>User {{ $ctrl.user.id }}</h3> <h2>{{ $ctrl.user.name }} {{ !$ctrl.user.active ? "(Deactivated)" : "" }}</h2> <table> <tr><td>Address</td><td>{{ $ctrl.user.address }}</td></tr> <tr><td>Phone</td><td>{{ $ctrl.user.phone }}</td></tr> <tr><td>Email</td><td>{{ $ctrl.user.email }}</td></tr> <tr><td>Company</td><td>{{ $ctrl.user.company }}</td></tr> <tr><td>Age</td><td>{{ $ctrl.user.age }}</td></tr> </table> ` });
Your controller:
app.controller('userDetail', function ($transition$, $state, UserService, users) { let $ctrl = this; this.uiOnParamsChanged = (newParams) => { console.log(newParams); if (newParams.userId !== '') { $ctrl.user = users.find(user => user.id == newParams.userId); } }; this.$onInit = function () { console.log($transition$.params()); if ($transition$.params().userId === '') { UserService.list().then(function (result) { $state.go('userlist.detail', {userId: result[0].id}); }); } } });
Handle new params by using $transition.on*
hooks on route change start:
An other approach would be to setup the right state
param before you change into your state. But you already said, this is something you don't want. If I would face the same problem: I would try to setup the right state
param before changing the view.
app.run(function ( $transitions, $state, CalendarService ) { $transitions.onStart({}, function(transition) { if (transition.to().name === 'mySate' && transition.params().firstAvailableDate === '') { // please check this, I don't know if a "abort" is necessary transition.abort(); return CalendarService.getAvailableDates().then(function(response){ // Since firstAvailableDate is dynamic // it should be handled as descript in the documents. return $state.target('mySate', {firstAvailableDate : response[0]}); }); } }); });
0 comments:
Post a Comment