-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
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
Allow absolute path change without destroying rendered components (Twitter-style modals) #703
Comments
I think this can be done with
I tried on a project and works like a charm. It's quite manual, so we may consider leveraging that. Edit: This went through an RFC for v4 and is merged and supported through the |
I think this is already possible with 2.0 because child routes can have a root path. ;) To achieve your example: {
// /linus_borg
path: '/:userId', component: Parent,
children: [
// NOTE absolute path here!
// this allows you to leverage the component nesting without being
// limited to the nested URL.
// components rendered at /baz: Root -> Parent -> Baz
// /vuejs/status/xxxxx
{ path: '/:organization/status/:xxxxx', component: Baz }
]
} |
@posva ...but pushstate will trigger a popstate event which will be picked up by the router... Which will re-route, which we don't want. |
@fnlctrl but this would to duplicate paths in the pathMap if I wanted that path as a root route and as a child route in multiple places... |
@LinusBorg Aside from that, I guess we can also try aliases?
|
Duplicates in the pathMap are not possible, the last one would override the previous one... So only one, the last one, would ever be recognizable. |
I see... but in that case where you want the path as a child route in multiple places, it would be mapping I think what we really need is a url rewrite feature like the backend servers have. It would be like an alias without being registered into pathMap. i.e. From this point on: |
Yes, that's describes pretty much what I had in mind, thanks :) |
I think going from |
I'm facing the exact same issue. that behaviour with |
any update on this topic ? |
I 'm on this, but haven't found the time to finish it with tests and all. Actually it's quite easy: You could, today, use the history API directly to change the URL with pushstate() or replaceState() and revert it back after e.g. the popup closes. Since the "popstate" event will only be called by actually clicking on the browser buttons, this can be achieved without interfering with vue-router. I'm working on providing a convenience function for this, but since I'll be on vacation until Nov. 2nd, this may take a while. |
This IS possible with vue-router 2, I just migrated my app to vue-router 2 to achieve this. This code (adapted from here) will actually do what you want: const User = {
template: `
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
`
}
const UserHome = { template: '<div>Home</div>' }
const UserProfile = { template: '<div>Profile</div>' }
const UserPosts = { template: '<div>Posts</div>' }
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, name: 'user',
children: [
// UserHome will be rendered inside User's <router-view>
// when /user/:id is matched
{ path: '', component: UserHome, name: 'home' },
// UserProfile will be rendered inside User's <router-view>
// when /user/:id/profile is matched
{ path: 'profile', component: UserProfile, name: 'profile' },
// UserPosts will be rendered inside User's <router-view>
// when /posts is matched
{ path: '/posts', component: UserPosts, name: 'posts' }
]
},
{ path: '/posts', component: UserPosts, name: 'posts-permalink' }
]
})
const app = new Vue({ router }).$mount('#app') <div id="app">
<p>
<router-link :to="{name: 'home', params: {id: 'foo'}}">/user/foo</router-link>
<router-link :to="{name: 'profile', params: {id: 'foo'}}">/user/foo/profile</router-link>
<router-link :to="{name: 'posts', params: {id: 'foo'}}">/posts (with user ID context)</router-link>
<router-link :to="{name: 'posts-permalink'}">/posts (permalink)</router-link>
</p>
<router-view></router-view>
</div> Basically you can go to the route named Now, if you refresh the page, as the URL has no information regarding the user context (here the To summarize, both Just like on Twitter. Hope this helps |
Will have to test this, looks good. Though I'm not sure weither the definition of the same absolute path in different places can produce unwanted side-effects e.g. for the pathMap. will look into this. If that works out as described though, I will be happy to close this issue. |
@LinusBorg The first route matching will be the one chosen. In this case, when going to About priorities between routes, in the vue-router documentation, see Dynamic Route Matching > Matching Priority:
|
Hm yeah that should do it ... |
@jeerbl I'm trying to implement this and here's what I found...
I'm guessing this is because the history push was not initiated from Vue Router so you lose the ability to tell Vue Router which route you want to use. |
@bradroberts Yes indeed. Got no solution for this yet. On Twitter it works fine. I'll try to find a way. |
This seems a little hackish, but it solved the back/forward button issue for me. In keeping with the example, the parent component (User) can intercept the route change (in this case when the visitor uses the browser's forward button) using the const User = {
beforeRouteLeave (to, from, next) {
if (to.name === 'posts-permalink') {
router.replace({ name: 'posts'});
} else {
next();
}
}
} It's worth noting that this will also affect route changes initiated by |
@LinusBorg I think it would still make sense to implement something like the BackboneJS functionality @nicolas-t mentioned. Just explicitly say to the router: "Ok, now just change the route silently and don't do any thing about it". This is much easier to reason about. I read through the example given by @jeerbl several times, and I still can't figure out how or if I can apply it in my situation. |
I was just about to post an issue similar and then I saw this. What @nicolas-t suggested seems like it would be perfect. Basically an "update the URL silently". I'm thinking this may be the easiest way forward? Since vue-router can operate in different modes, I would like to use vue-router to always change the URL. This way it can work in history mode or the hash fallback thing. My scenario is just paging through the comments section of a page. I would like to change the comment page query param without refreshing the whole page. This way the URL could be shared with others and that particular comment page would show. |
Any updates on this? I really need this. Right now I have both routes for the modals (like login, register, product, etc) and in place rendering. But I have to sacrifice the user unable to click back button for closing the modal, and have to add a perma-link box, because in place rendering cannot update the browser url :( |
Would love something like this feature nicolas-t was talking about. Im building a product modal where a user can navigate between different products in the same modal. |
Few notes regarding @tmiame solution #703 (comment) NOTE 1 but for now we have: It throws 2 warnings in console regarding this part of code:
Warnings:
Fixes:
NOTE 2 NOTE 3
When needed modal is opened, in console I see:
This is due to the fact that my modal does not have
Be careful, if you use urls ids, it will replace " NOTE 4
with the
... there is a problem -> if I open a modal inside |
Hello, I also have same kind of needs:
I have written a sample solution to solve these problems, but don't know if I'm creating new ones at the same time. I'm using a custom "router-wrapper" component to keep components untouched when needed (that is when there is no matching URL for the router view). This solution can be tested here: https://cdpn.io/ksurakka/debug/RwwKyPy#/product/123 And source code can be seen here (if you are not familiar with CodePen): https://cdpn.io/ksurakka/pen/RwwKyPy#/product/123 (Updated more recent code pen versions, now modal+history cleaning works better) |
@ksurakka Good solution, non tricky. I am looking into this |
works if you really need it. |
@dxc-jbeck There are some side effects. For example |
Has this issue resolved? |
It looks like @posva is looking into it for an upcoming release – https://twitter.com/posva/status/1242513301726203904 |
You could take a look at my solution. A bit hacky with the meta property, but I use it and it works just fine with no breaking changes: #3041 |
Hey, this issue has "fixed on 4.x" it's already in beta, documentation is there but I can't find any information on new features that enables this in v4. Am I missing something? |
Can we all have some kind of a short mention of how to achieve this with v4? We are ready to upgrade just because of this feature, however it's unclear how to implement it with new version. Thanks! |
As a tangent, you can successfully use replace to swap between aliased URLs without impacting your state, and successfully mutating your history. |
@stepanorda Unfortunately, I do not know how historyState should be handled in production. For Vue2 people:
EDIT: got a quick implementation of Posva's. I just used window to store historyState. For some reason, however, refreshing the page with an open modal breaks it EDIT2: The bug was fixed btw, you just can't use lazy-loading in the router. |
|
I'm closing this in favour of #977 because it's a solution to the same problem that avoids hacking router navigation. There is an e2e test that shows how to implement modals with v4: https://github.com/vuejs/vue-router-next/blob/master/e2e/modal/index.ts |
@dxc-jbeck
|
This is an implementation of the modals with the new possibilities of vue router 3. See: vuejs/vue-router#703 (comment) for a better explanation and the linked example implementation: https://github.com/vuejs/vue-router-next/blob/master/e2e/modal/index.ts
For anyone still having this problem, try memory mode:
This may not solve all of your problems but if you just want to change the route but keep the URL, this will work. |
Hello, It landed in react via next.js 13.3 and it's called parallel routes. Doc : One URL can now map to multiple component trees. |
Also being implemented in Svelte: Source : https://twitter.com/Rich_Harris/status/1657092537771778055?t=R-8XL8NdAU8Y7mL_19oeMw&s=19 |
Any official docs to this issue? I see all links that provided are now 404 status 😢 |
@RomanHrynevych this issue was closed in favor of #977 as you can see in this comment: Issue #977 is yet opened, which might imply this feature is not yet available. I am not sure, as I ended up implementing something else to solve my problem at the time, but am still subscribed to this issue updates. By something else, I don't mean I ended up implementing a similar feature, on the app I was working at the time I changed its workflow to avoid needing this feature. |
I was looking to implement Twitter-style modals in Vue 3, but a lot of the examples and demos I saw above assume that a particular modal must be the child of a particular route. However, the drawback is that if a different main view has a link to navigate to the modal, the main view will also change to be the one the modal "belongs" to. In my use case, I wanted a modal to:
In Vue 3, I solved it with a dedicated named view for modals:
|
For those who are still stuck with this issue, you can do something similar. It will change your URL without reloading the page and also update the router object. const route = useRoute();
const router = useRouter();
function replaceUrl(){
const newRoute = router.resolve({ name: route.name as string, query: {} });
router.currentRoute.value = newRoute;
window.history.replaceState('', '', newRoute.fullPath);
} |
Status Quo
In vue-router, the URL is the single source of truth. That means that no matter weither the user triggers a route from an external link, by typing out the URL himself or by clicking on a
<router-link>
inside our app, the same route if the URL is the same, then the same routes will be matched, and the same components will be rendered - no side-effects are taken into account.I consider this to be a sensible, good approach. But there are situations where this behaviour is undesired:
The problem
If you go to Twitter's website, and open a tweet from any place (your own timeline, someone else's timeline, a list ...), three things happen:
twitter.com/linus_borg
and went totwitter.com/vuejs/status/xxxxx
when we opened the modal, my profile's timeline is still in the brackground.The user could simply copy & paste the tweets URL, post it somewhere, go back to twitter, close the modal and continue browsing my profile. Great!
Butr currently, this is not possible with vue-router. With vue-router, we would either have to
/linus_borg_/status/vuejs/status/...
or something.Downsides
This breaks vue-routers philsophy of the URL as the only source of truth, because opening a tweet from timeline leads to a different view than opening the tweet's URL directly.
Proposed Solution(s)
Here this proposal becomes thin, and I didn't add the
discussion
label for nothing:I don't really have an idea on how to approach this. I know that it's probably very hard to implement this in vue-router as it working different to the whole route-matching pattern etc, so I'm open to ideas.
Maybe, we could at least implement a way to "pause" the router's URL change callback? So the user can "deactivate" the router momentarily, change the URL himself, show the modal, and on closing the modal, reactivate the router?
Or maybe this could be accomplished with named router views, if we allow route that only point to a named view , so the "normal components, e.g. the profile, stay rendered, and we show the overlay in a named view? (tested this, doesn't work right now).
Anyways, I think this wold be a great feature for many scenarios (Spotify's uses this for its ovelays showing album / playlist / artist views anywhere, for example)
So, what do you guys think? Any opinions? Ideas for solutions?
The text was updated successfully, but these errors were encountered: