Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In some usecases, changing parent route url parameter should not reset child route #7378

Closed
cporte opened this issue Mar 2, 2016 · 13 comments

Comments

@cporte
Copy link

cporte commented Mar 2, 2016

In some applications, we may want to have a multiple step edition of a complex object, with multiple sub-screens
Imagine the following routes:
/ //the home page
/help //an help page
/projects //the page to CRUD the projects
/projects/:idProject/results/per-categories/top5chart //an hidden page far far away but still useful

The user may want to change the currently selected projet (in a side menu for exemple) without loosing the current route.

Currently, the only way to achieve this behaviour easily (i.e. with router-link) is to use queryParams for the idProject

It would be great if there were a way to configure the routing to permits this behaviour without using queryParams (and ugly URLs)

@brandonroberts
Copy link
Contributor

@cporte you can the routerCanReuse lifecycle hook. For a given route component, you can tell the router to reuse that component instead of re-instantiating it. If the route component is reused, the routerOnReuse lifecycle hook is called where you can get the new idProject param if you need to do something with it.

@cporte
Copy link
Author

cporte commented Mar 3, 2016

Hum, maybe I failed when I tested this solution, but it didn't work.
If I understood correctly how things work, the routerCanReuse callback is called only called during a navigation. The issue I have here is the link generated by the routerLink directive.
In my exemple, let say I have a menu created using *ngFor on projects and each entry have a [routerLink]="['projectExploration', {'idProject'= p.id}]" directive. The generated href will be /projects/462/ and after the navigation the URL will be /projects/462/, and not /projects/462/results/per-categories/top5chart as I would like.

Or is there a special mecanism keeping the child routes when the routerCanReuse hook returns true and I just didn't figured out how to make it work?

@brandonroberts
Copy link
Contributor

@cporte It works if you're visiting the same route component multiple times, so going from /projects/462/ to /projects/463/ would work with routerCanReuse returning true, as well as going from /projects/462/results/per-categories/top5chart to /projects/463/results/per-categories/top5chart would work. Going from /projects/462/results/per-categories/top5chart to /projects/463/ would cause a reload because your visiting a different route component. You could generate links in the menu that match the /projects/462/results/per-categories/top5chart if that's what your intended result is.

@cporte
Copy link
Author

cporte commented Mar 7, 2016

Hum, I don't know how to explain better the use case. I would like to go from /projects/462/results/per-categories/top5chart to /projects/463/results/per-categories/top5chart without having to go to /projects/463/results/per-categories/top5chart before /projects/462/results/per-categories/top5chart.
Like "I'm authenticating, I'm exploring my project on /projects, I pick the project 462 so I'm at /projects/462/, I'm exploring results of this project and end up at /projects/462/results/per-categories/top5chart, and now I would like to see the same thing in one clic for the same project using a "project exploration panel" (present all the time), so I click on the 463 project and I'm at the URL /projects/463/results/per-categories/top5chart.
And it would be perfect to do that without having the children routes leak in the parent level project exploration panel. I would like only have [routerLink]="['projectExploration', {'idProject'= p.id}]" in the exploration panel, and let Angular handle the fact that in every case, the final link have to be the exact same as before, except for the idProject parameter.

Basically, I was able to do a similar thing by using queryString instead of route parameters (so having URL like projects/results/per-categories/top5chart?idProject=462, but the "bug" is now fixed b47f80e #7298 so I'm kind of stuck here.

@brandonroberts
Copy link
Contributor

Ok, I see. Its a matter of how your routes are setup.

@RouteConfig([
 { path: '/', ... }, // home
 { path: '/help', ... }, // help
 { path: '/projects', ...}, //the page to CRUD the projects
 { path: '/projects/:idProject/results/per-categories/top5chart', ...} //an hidden page far far away but still useful
])
class App {}

If your routes are setup like this, then your routes under your project path won't be grouped. If you want to group all project related paths, you would do:

@RouteConfig([
 { path: '/', ... }, // home
 { path: '/help', ... }, // help
 { path: '/projects/:idProject/...', component: Projects, name: 'ProjectExploration' } //the page to CRUD the projects
])
@Component({...}) // include a <router-outlet></router-outlet>
class App {}


@RouteConfig([
 { path: '/results/per-categories/top5chart', name: 'TopResults', useAsDefault: true } //an hidden page far far away but still useful
])
@Component({...}) // include a <router-outlet></router-outlet>
class Projects {}

This allows you to build your child so they all include the project ID.

<a [routerLink]="['ProjectExploration', {idProject: 462}, 'TopResults']">Project 462 Top Results</a>
<a [routerLink]="['ProjectExploration', {idProject: 463}, 'TopResults']">Project 463 Top Results</a>

@cporte
Copy link
Author

cporte commented Mar 7, 2016

Yes, this would be my setup for the components. My issue is more that the project selector is at the parent level and is not supposed to be aware of children routes ().
It makes perfect sense that different parent routes should have different children route. Keeping the same example, it's totally normal that /help/results/per-categories/top5chart doesn't make any sense.

But as soon as a route has a parameter, It's kind of tricky to decide that if the parameter change, the route is considered totally different and we have to reset both the children parts and the query parameters.
Basically, as soon as ":parameter" is declared on a route, we know that the same type of child component will handle this parameter.
It would be really great to have a way to create a navigation link saying "here, this link will only change a parameter of one route, keep everything else exactly as before, the other route parts, the query parameters, the components, etc"

@brandonroberts
Copy link
Contributor

Ok. You could look into the routerCanReuse lifecycle hook to tell the router to reuse route components across navigation even if the project ID changes.

@cporte
Copy link
Author

cporte commented Mar 9, 2016

Thanks, but I think I still didn't made my request clear.
So, here is some (dart) code, it will be easier to explain : http://pastebin.com/Pnz0KG6H

Basically a changed a bit URLs and component to be clearer.
What I would like to do is simple: in this example, when I'm at http://localhost:8080/index.html#/project/3/year/2015/month/2, I would like to go to http://localhost:8080/index.html#/project/1/year/2015/month/2 with the top routerLinks, defined in the RoutingPage template.
Of course, I would also like to be able to keep the month when I change the year.
The RoutingPage is not supposed to know what are its children. RouterCanReuse or not should not be related to this. I may would like to keep the same 2nd part of the URL (/year/2015/month/2) but with new components for whatever reason.
And I would like to avoid having the children routes leaking in the parent.
And, of course, I would like to use what Angular 2 can provide. A solution would be to replace routerLinks with calls to NavigateByUrl, capture the current URL just before the call, and replace the part of the URL that need to change. But it seems ugly and dangerous.
As mentioned, before Angular beta 8 I could use the QueryParams, but they now have the same reset-if-not-explicitely-declared behaviour.

I hope this time my explanations are less confused :)

@brandonroberts
Copy link
Contributor

Code always helps 😄 ... The way the router does deep linking goes against what you're trying to do here. To deep link you have to provide the entire route path from parent to child. Based on what you've described, it sounds like you need dynamic router links on your RoutingPage.

You would need to use something to store the route array with the current project, month, year, etc and update that route array when you visit one of the child routes, and your router links in your RoutingPage reference that array with the correct project. That way you're RoutingPage doesn't have to have explicit knowledge of the children, but they are provided with the route array that creates the deep link. I can try to put a plunker together if this isn't clear enough.

@cporte
Copy link
Author

cporte commented Mar 9, 2016

Thanks for your reply. The question now is "should the router cover this kind of requirements?". I would tend to keep this enhancement request, maybe cleaning it for better understanding, and see how the other users see this use case. For what I saw in some other github issues the new router have other limitations (coupling between components and routes, lack of input/output support between parent and children, difficulties to access all the parameters of all routes, etc) and if at some point there is a redesign of the router in the future I would love to see my request discussed along with the others.
Where I work we create complex web applications (this is way we picked Dart + AngularDart a couple of years ago), and it's common to deal with more than 4 URL parameters, sometimes not necessarily linked one to another but handled by different components to avoid having too many things to handle in one component. So keeping the current parameters while changing one which is not the last one is important for us.

If you have some time, a plunker would be nice, but I see the concept. The only point I don't see now is how can I track the current routes in a service (or in the root component). Last time I tried to inject some routing stuff (like Location, or things like that) and listening to them I wasn't able to track the URL changes

@cporte
Copy link
Author

cporte commented Mar 11, 2016

One additional consideration, as I'm studying Angular 2 more in details to find workaround.
I just realized I might have taken the problem on the wrong end.
Let's imagine that the project can be selected in a side menu. This selection is not really linked to the whole date selection, we might consider project selection and date selection to be independent from each other.
So, concept based, the solution would be probably to put the project selection in an auxiliary route, and use a service to store the currently selected project (this service could be than used in the other components).
That way, the selected project is kept even if the main route changes, and the selected date is not lost in case of project change.
The Aux Routes are kind of broken currently, so I can't really use them as I would like, but I think it can do the trick. I think having a special routerLink or a special symbol in the DSL to just change one parameter in the current URL could still be nice, but maybe not as important as I thought.

@vicb
Copy link
Contributor

vicb commented Sep 26, 2016

obsolete

@vicb vicb closed this as completed Sep 26, 2016
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 9, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants