-
-
Notifications
You must be signed in to change notification settings - Fork 5k
[next] Different behaviour for default subRoute when using path vs. named route #629
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
Comments
This is expected behaviour and intentional. If you want to render the default path via name, you have to use it's name, not the parent name. |
Ok, thank you! Can you explain the reasoning behind that? I think the uri should represent the state of the app. If |
When using a named route-link, the route record is fetched by that name from a nameMap object. https://github.com/vuejs/vue-router/blob/next/src/create-matcher.js#L33 When we use a path, the matched route is retrieved from a pathMap object. Here we get the defautl child route. https://github.com/vuejs/vue-router/blob/next/src/create-matcher.js#L33 That's why it works differently I think. Maybe the solution is as easy as this: After getting the record by name, get the record again from the pathMap using ? |
I just ran into this today. I agree that it is pretty unintuitive to have the named route-link resolve differently than the path when a default child route is defined. @LinusBorg your solution seems like it would produce intuitive results. |
after some discussion we will solve this with a warning: When the dev uses a named route that has a default child route, there will be a warning in the console. Reasoning:
We will reference the commit here when it's done. |
PR provided with #641 - currently in review. |
I'm not sure that refraining from using links to parent is the right thing to do. Say I have |
Oh lol @inca so true... |
) * fix #629 - add warning when named route has a default child route. * Improve warning message. * this fails me * debug log * corrected Object.keys() mistake, shrunk test case to one case, still fails * only warn when not in production mode. * remove the helper, solved it with a normal jasmine spy * ad child component again * remove msg variable, check with `toMatch()` instead. * remove unnecessary test case. * remove unnecessary const, create it in beforeAll instead. change testacy to revert to this.maps instead of maps * add test case for console.warn spy * remove debug log * add replace plugin for rollup to define "globals" like with web packs DefinePlugin. * add replace plugin to rollup config and set production env variable in pm build script. * Importe test case to test warning in production and development mode. * fix unrelated lining bug: debugger statement in a file. * cleanup unnecessary statement from test case
@inca that's indeed a common scenario that would clash with this fix. I will leave this issue open and think about how to deal with this. |
@inca on second thought, this is not an issue. You would link to the default child named route like this: <router-link :to="{name: 'item.summary'}"></router-link>
// path: `/ítem/12/` -> link is active now if you link to another child of that parent: <router-link :to="{name: 'item.other-child-route'}"></router-link>
// path: `/item/12/other-child-route` -> link is active ... the first link with the default child route would also still be active, because the non-exact match is still true. Little demo: https://jsfiddle.net/Linusborg/r0jsw888/2/ |
Ah, nice @LinusBorg. |
Haha, yep, that works because of paths matching, but tbh that's a bit unintuitive, too :) I mean, just think about it: you have a link to child state which is active while the state is not technically active (b/c its sibling state is active). Makes my head spin a bit 😄 |
No, in child routes, |
@inca I admit that this is not 100% intuitive when thinking from the perspective of the named route. But the real starting point should always ne the path. The URL is the source of truth. From that perspective, it makes sense that the parent and the default child have the same "active" behaviour - they have the same path. In that way it behaves exactly like the browser does. As you can read further up, we thought about changing the behaviour to acutally match the parent named route to the default child, but that would be both a breaking change and also confusing in its own way. |
Yep, unfortunately. (I can elaborate, but it's not exactly related to this issue) Named routes are kinda inevitable if params are involved, which makes this piece of navigation a bit hard to reason about. Conceptually, one could also argue that upper-level navigation component should only point to "item" without referring to an explicit substate. In other words, only router decides which subroute is default, not router and links. Since I'm a big fan of old Angular's ui-router I would propose adding "abstract route" feature. The idea is pretty intuitive: simply add |
On a second thought, I think we can make one of the following assumptions without introducing
Reasoning: the problem obviously stems from the combination of two facts: 1) URL (pathname) is the source of truth for routing composition and 2) both parent and default child share the same URL thus introducing a degree of ambiguity into the source of truth. So in case the first solution is preferred the fix is somewhat simple: first resolve the named route to path using |
This is exactly the solution I had in mind at first (see last paragraph here), but it is technically a breaking change, and also confusing in its own way: When the dev explicitly wants to go to the named route This sort of implicit behavior is generally not a good idea and something we try to get rid of in Vue wherever we find it. I talked this through with Evan and the others and we came to the conclusion that we rather warn the dev of the behavior that navigating to the named parent has, and encourage to explicitly use the default child. Then the dev wont be surprised by finding the "wrong" data in his $route. |
I am very sorry for being such a stubborn. But there's clearly an inconsistency there, so I don't believe that warning is an adequate solution. Let me try to explain:
This contradicts to the "URL is a source of truth" principle mentioned above. Both parent and its default child share the same URL, so going to parent (only by name!) will leave you with broken transition (unrendered ) and a warning; but refresh the page — and you land to the parent.defaultroute (b/c this time router actually uses path instead of name). So we decide to condemn a dev for being not explicit enough by issuing him a warning. But actually it's not dev's fault, because router allows multiple routes to map to the same URL and provides no rule to tell which one should be rendered. As for the implicit vs. explicit, I totally agree that explicit way is a good one, but 1) it shouldn't violate the separation of concerns (see my navigation-to-item-not-to-item-summary argument above) and 2) this largely depends on interpretation: for me Bottom line is, if the rule is explicitly stated in docs and covers real usage scenarios everything should be fine. |
@inca Some side notes:
vue-router's nested route is different by design to ui-router's counterpart because:
But in vue-router, child routes are considered optional, and the parent route can get activated itself.
So true... that's the exact reason I removed all the route names in my prod app, and switched to manually spliced full paths. I wonder if we can deprecate names in vue-router 3.0 since it's only bringing troubles all the way (active class, child routes, etc.), making roughly 31% of the issues here ( (14+137)/(37+448) as of today ). To sum up, I agree with @LinusBorg that a warning is enough, because
As stated in the first note, child routes are considered optional, and this implicit behavior may also bother devs who want to show empty child route. |
I think there's room for different perspectives. I can see some valid arguments in what @inca explained. I think both ways are a possibility - and neither will be a no-go. So I think for the moment the solution we provided works, but we should further discuss weither it might be worth it to match the default child route, after all. Thanks for the input @inca, and no worries about being stubborn, that's nessessary sometimes ;) |
@LinusBorg Thank you! @fnlctrl not really getting the "child routes are considered optional" thingy... Personally I only use routes nesting only for shared stuff used by children (layouts, data fetching, beforeEnter hooks, common URL prefix, nav highlighting). The parent itself doesn't have meaningful content, only I wonder, what's the usecase for rendering an empty parent that I've missed? |
Parents actually can have meaningful content, other than just a |
A simple one would be:
Of course one could solve these trivial example without routes and might even prefer to, but it seems obvious to me that there can be similar scenarios where one wants to have a route that has children, but doesn't need a default child. |
@LinusBorg Thanks for examples, makes sense. Seems like it's safe to assume parent + default child = abstract parent, but I'm happy to leave this up to you guys :) |
btw, I just discovered this: https://github.com/vuejs/vue-router/blob/next/examples/redirect/app.js#L31, so parent can explicitly redirect to its default child — works like a charm! |
@inca That's actually a very smart solution to this issue! Explicit and intuitive. (It'a pity we didn't come up with it before... it just makes so much sense) @posva I think we should add it to the guides! |
Having read all of this, I agree with the direction of the dev team, and the redirect suggested by @inca resolves my use case. But I do think it would still be nice to have some kind of option, like |
I will close this for now, as i think we have an understanding that things will stay the way they are for now. We can revisit this later if the need arises. |
Uh oh!
There was an error while loading. Please reload this page.
I have a nested routes with a default component:
I'd expect for
HomeIndex
to be rendered intoHome
's<router-view>
when i enterHome
, but when i navigate to it using:to="{ name: 'home' }"
, onlyHome
is rendered.Using
to="/home"
directly works as expected.Vue.js & vue-router.js version
2.0.0-rc.4, 2.0.0-rc.4
Reproduction Link
https://jsfiddle.net/jwahdatehagh/rdtgfrfb/
The text was updated successfully, but these errors were encountered: