可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm having one parent state that has two children's state inside that I'm going to show one state based on the URL
.
Out of those two states
one is having to parameters like param1
and param2
, I have use params
option of ui-router inside state definition.
State
$stateProvider.state('tabs.account', {
url: '/account',
views: {
'content@tabs': {
templateUrl: 'account.html',
controller: function($scope, $stateParams) {
//This params are internally used to make ajax and show some data.
$scope.param1 = $stateParams.param1;
$scope.param2 = $stateParams.param2;
},
}
},
params: {
param1: { value: null }, //this are optional param
param2: { value: null } //because they are not used in url
}
});
If you look at my route the params option is not really introduced inside the URL
, that's why I'm considering then as optional.
Problem
Look at plunkr, I've shown two tabs Account & Survey,
- Click on Survey tab, then add some data in the
textarea
which are shown.
Click on Go to Account that will pass those textarea
values to
the other Account tab by doing ui-sref="tabs.account({param1: thing1, param2: thing2})"
on the anchor
Now you will see the param1
& param2
values on html which has been assigned to scope from $stateParams
- Now again Click on Survey tab, you will land on the survey page.
- Just click browser back, you will notice that
param
value is not getting null
.
Problem Plunkr
I believe you got what I wanted to ask, why the optional parameter value has not been store? as they have been a part of state.
I know I can solve this issue by below two solutions.
- By creating one service that will share data between two views.
- By adding parameter inside the state URL. like
url: '/account/:param1/:param2',
(But i wouldn't prefer this)
I already tried angular-ui-routers sticky states
but that doesn't seems to work for me. What is the better way to this?
Is there any way by which I can make my use case working, Any ideas would appreciate.
Github Issue Link Here
回答1:
I would move the params
definition to the parent state, so as to share the optional state params between your two child states.
The child states will inherit the $stateParams
from your parent, as such there is no real 'workaround' needed.
Simply inject $stateParams
as per usual in your child controllers and you will have full access to the params being passed around. If you don't want to utilise the params in a specific child state, simply avoid injecting them.
This works with;
- Back button
- Forward button
- ui-sref (without params (will keep as-is))
- ui-sref (with params (will overwrite))
$stateProvider
.state('parent', {
params: { p1: null, p2: null }
})
.state('parent.childOne', {
url: '/one',
controller: function ($stateParams) {
console.log($stateParams); // { p1: null, p2: null }
}
})
.state('parent.childTwo', {
url: '/two',
controller: function ($stateParams) {
console.log($stateParams); // { p1: null, p2: null }
}
})
If you at any point want to clear the params while travelling within the state tree of parent
, you would have to do so manually.
That would be the only real caveat I can see by using this solution.
I realise manual clearing may not be desirable in the case you present, but you haven't taken an active stand against it, as such I feel the suggestion has merit.
updated plunker
回答2:
One workaround solution is to cache the state params and conditionally load them when entering the tabs.account
state. UI Router state config actually lets you provide an onEnter
callback for these types of "do something on entering the state" situations.
Here's the basic logic using localStorage as the cache, with working Plunker here:
- When you enter the
tabs.account
state, check for your state params
- If you have them, cache them to local storage
- If you don't, load them from local storage into
$stateParams
Here's an example code snippet for reference (taken from the Plunker):
$stateProvider.state('tabs.account', {
...
onEnter: ['$stateParams', '$window', function($stateParams, $window) {
if($stateParams.param1) {
$window.localStorage.setItem('tabs.account.param1', $stateParams.param1);
} else {
$stateParams.param1 = $window.localStorage.getItem('tabs.account.param1');
}
if($stateParams.param2) {
$window.localStorage.setItem('tabs.account.param2', $stateParams.param2);
} else {
$stateParams.param2 = $window.localStorage.getItem('tabs.account.param2');
}
}],
...
}
One caveat is that your params will persist indefinitely (e.g. across refreshes and sessions). To get around this, you could clear out the cache on application load like in app.run
.
One last note is that in the Plunker, I'm accessing local storage directly (through the Angular $window
service). You might want to use some AngularJS module - I've used angular-local-storage in production.
回答3:
I believe that what you want to achieve is not possible without using one of the two solution you provided.
The browser back-button is just keeping the URL history. He have no clue about the ui-router internal states and will just force the URL to change.
Forcing the URL to change will trigger internal ui-router machine but unfortunately ui-router will see the URL change the same as if someone would have change the url by hand.
Ui-router will fire a new route change to the route pointed by the URL. That mean he doesn't know you wanted to go "back" and will just change state to the new one without any parameters.
Summary
Clicking on back button will fire a state change to a new state according to the URL instead of going back to the previous state.
This is why adding the params to the URL solve the issue. Since the URL is discriminatory you'll finally land on the state you wanted.
Hope it helped.
回答4:
To me this sounds as X Y problem. There are suggested ways to make state params persistent whereas the problem is located out of this surface.
By definition of the question there is data that should be kept independent of states. So it is kind of global relative to states. Thus there should be a service that keeps it and controllers of both states maintain it as required. Just don't pass data that is out of one state's scope in state params.
Sticky states would fit this approach easily since it allows to keep DOM and $scope
while another state is active. But it has nothing to do with state params when the state is reactivated.