Angular ui-router. Child state parameter based on parent state parameter - javascript

I'm using ui-router with Angular 1.6. The root state is the site language (/en, /nl, /de).
Most child states are the same for all languages. However, for some states the names of the child states are translated:
/en/english-state/common-state
/nl/dutch-state/common-state
/de/german-state/common-state
How can I make sure the middle state always matches the lang parameter (en/nl/de) in the parent state? I'd like to use ui-sref without providing the middle state; this should be filled automatically based on the language. When the middle state doesn't match the language in the parent state it should be changed automatically to match the language in the parent state.
I can't find an intuitive solution in the ui-router docs, but perhaps I'm missing something. Anyone done this before?

After a while, I've decided to solve it like this, which I think is the cleanest way to do it:
(Note: This is a simplified version. I've replaced functions with strings , omitted dependencies and removed lines, this is not-working code, it's about the idea).
Expose the language variable in the resolve part of the root state, something like this:
$stateProvider.state({
url: '/{lang:(?:en|nl|de)}',
name: 'root',
[ ... ]
resolve: {
language: function() { return $stateParams.lang }
}
In the child state, check if the URL matches the language:
$stateProvider.state({
url: '/{mySlug:(?:english-state|dutch-state|german-state|__my-state__)}',
name: 'root.child',
resolve: {
checkSlug: function () {
$timout(function() {
if (notMatchesLanguage($stateParams.mySlug, language)) {
var params = JSON.parse(JSON.stringify($stateParams));
params.mySlug = correctSlug;
$state.transitionTo($state.current.name, params, { notify: false });
}
}
}
}
Note the added slug in the mySlug list, __my-state_ _
Now in every ui-sref, or $state.go we can use __my-state_ as mySlug parameter. This strange slug won't match any language and will be replaced with the matching state slug.

Related

Changing a parameter in Vue 3 router

I was trying to change a route parameter with Vue router.
My route is the following :
/document/:id?/:lang?/edit
Id and lang (language) are the parameters.
I want to change the parameter lang using JavaScript without altering the rest of the route. I tried applying the following solution (found here). The solution was made for Vue 2 and not 3. So I tried to make an updated version.
This is my code :
let params = this.$route.params
params.lang = 'en'
this.$router.push({params:params})
When I execute my code, there is no change in the route parameters nor an error logged in the console. Does anyone know a solution to this problem in Vue 3.
Thanks in advance.
It's a bad way to change your route parameter like this.
Instead, replace your route with the same name, same id, but different lang:
this.$router.replace({
name: this.$route.name,
params: {
id: this.$route.params.id,
lang: 'en' // or whatever you want
}
})
Don't forget to watch for the route changes if needed:
watch: {
$route(to, from) {
// if anything needs to be done when the route changes
}
}

How do I specify a state parameter on a state in the middle of the route (ancestor of target state)?

Spent hours googling and combing through the docs angular ui-router docs to no avail...seems like this should be really basic.
The quick version
Looking for something that I imagine might look something like this
<a ui-sref="parent({param: value}).child">go</a>
or this
$state.go("parent.child", {parent:{param: value}})
- the idea being that param gets applied to parent, not child, resulting in a url such as /parent/value/child
The long version
THE SETUP:
Let's say I have a state that takes a parameter, projectId, like so:
.state('manage.project', {
url: '/project/:projectId',
templateUrl: 'fragments/projectTemplate.html',
controller: 'projectController'
}
...then way down the state tree, I have one of many child states that depend on on that parameter:
.state('descendantOfProjectState.grandchildState', {
url: '/projectInfo',
templateUrl: 'fragments/projectInfo.html',
controller: function($state){
var projectId = $state.params.projectId;
}
}
Normally, one would first visit the parent:
$state.go('manage.project', {projectId: 123})
At this point the URL would look something like
/manage/project/123/foo/bar/projectInfo
From there, navigating to a child state is as simple as
$state.go("manage.project.foo.bar.descendantOfProjectState.grandchildState");
and the projectId is accessible via $state.params by virtue of being in the URL - no need to configure every single child state to explicitly expect the projectId (wondering if this is my fatal flaw).
THE PROBLEM:
Now the need has arisen to jump to that grandchild state, without first visiting the parent state. That is, I want to go directly to
/manage/project/123/foo/bar/projectInfo
from within the app (so, I want to go to the state, not the url).
How can I specify the projectId parameter for the project state, when it's in the middle of the route I'm navigating to?
$state.go('manage.project.foo.bar.descendant.grandchild', {projectId: 123})
doesn't inform the the ancestor state that needs the id.
How can I, in one fell swoop, tell ui-router to go directly to that grandchild state while passing the projectId to manage.project state which is in the middle of the route?

naming states in angularjs ui-router

instead of using ng.router i am switching to ui.router in my angularjs application.
I came across a problem about state names in config. When I use '.' character in state name, routing doesn't work. Code I gave below doesn't route to 'views/subcategories.html' when I enter "localhost/#/categories/5" in my browser url.
$stateProvider
.state('categories',
{
url:'/categories',
controller:'CategoriesController',
templateUrl:'views/categories.html'
})
.state('sub.categories',{
url:'/categories/:subID',
controller: 'SubCategoriesController',
templateUrl:'views/subcategories.html'
});
However when I use 'subcategories' instead of 'sub.categories' it works. I am asking this question because I saw state names with '.' in a lot of tutorials about ui.router. Do I miss some detail here?
with dot syntax in name of states, you can a nested states. The nested state, inherit params and urls from parent state.
First, you need start the name of nested state, with parent state:
'categories.sub' // instead 'sub.categories'
Then, to localhost/#/categories/5, set your nested state like below:
$stateProvider
.state('categories',
{
url:'/categories',
controller:'CategoriesController',
templateUrl:'views/categories.html'
})
.state('categories.sub',{
url:'/:subID',
controller: 'SubCategoriesController',
templateUrl:'views/subcategories.html'
});
Note, the state 'categories.sub', in url, dont need '/categories', because this is inherit from parent state 'categories'
In ui-router, periods are used to indicate nested states.
Using sub.categories would make the categories state a child of the sub state.
More info can be found here

How to persist optional state parameter on browser back in ui-router?

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
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
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.
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.
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.

AngularJS ui-router

AngularJS state machine extension ui-router declares a directive that converts routes with attribute ui-sref into href paths and populates it with requested variables. Is there a way to access the same route parser from a scope?
Update
I am looking for a hopefully built-in yet undocumented resolver (or a way to get the same outcome) that gives the path to a named argument. In the spirit of a named route:
<a ui-sref="management.person({personId: 1})" />
Which matches a route
$stateProvider.state('management', {
url: '/absolute/part'
});
$stateProvider.state('management.person', {
url: '/relative/part/:personId'
});
and outputs #/absolute/part/relative/part/1 - and in case I switch to use ! fragment, all the URLs are converted. Directive itself does this already, but its arguments cannot be constructed dynamically.
As of March 2015 I got 'UrlMatcher is not defined' for Chad Robinson's answer. But I succeeded by injecting $urlMatcherFactory and replace urlParams in a ui-router template url.
$urlMatcherFactory.compile("/about/:person").format({
person: "bob"
})
ui-router provides several services in an API that you can use to do things this. Try one of these examples:
From http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$state
var url = $state.href('about-person', {
person: "bob"
});
From http://angular-ui.github.io/ui-router/site/#/api/ui.router.router.$urlRouter
var url = $urlRouter.href(new UrlMatcher("/about/:person"), {
person: "bob"
});
These two patterns convert state names and URL templates into fully formatted URLs. There are additional API calls available as well.

Categories