-
-
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
change current component used by router-view
without changing current URL
#977
Comments
router-view
change
@fnlctrl I think you misunderstood my meaning, I means "Trigger router-view change without changing current URL" |
router-view
changerouter-view
change without changing current URL
@fenivana Sorry for the delay. In that case, it looks like a very app-specific behaviour. I actually prefer giving the user a clickable link that brings him to the right place (or something like try again) instead of asking him to reload the page. |
@posva The point is the current URL should not change, rather than a URL like And I've tried your suggestion in Chrome console but without success:
|
Oh, sorry. It actually needs more parameters. The path being one of them and name being optional actually xD vue-router/flow/declarations.js Line 67 in 154e269
|
I think it's a useful feature. We could expose a method on the router instance: router.replaceView({ name: 'error' }) and add support for the name syntax too (as a router-link) It also makes easier to handle graceful degradation. So basically what we actually need is a way to modify the navigation to a different view while keeping the url. Most of the time this is something that would be used in the @fnlctrl @yyx990803 What do you think? I'm not satisfied with the API below because of the 2 different ways of doing it --> People may want to use the callback to replace the view: |
Hmm.. Maybe we can directly pass components instead of routes to import FooView from 'foo';
router.replaceView(FooView)
router.replaceView(Vue.component('regiesterd-global-component')) The only problem would be importing the components everywhere import FooView from 'foo';
import BarView from 'foo';
const router = new VueRouter({routes: [
{path: '/foo', component: FooView},
{path: '/bar', component: BarView}
]})
router.beforeEach(() => {
//...
router.replaceView(FooView)
}) |
I think we can add a new Router({
mode: 'history',
routes: [
{
path: '/',
component: () => System.import('./layouts/default.vue'),
children: [
{ path: '', component: () => System.import('./views/Index.vue') },
{ path: 'foo', component: () => System.import('./views/Foo.vue') },
{ path: 'login', abstract: true, component: () => System.import('./views/Login.vue') },
{ path: 'error', abstract: true, component: () => System.import('./views/Error.vue') },
{ path: '*', component: () => System.import('./views/HTTP404.vue') }
]
}
]
}) Here page |
It still feels like a hack to me. This is basically a piece of global state ( I'm not sure if I understand what you mean by the following:
|
Yes ! I want this feature please 🙂 router.replaceView(my404PageComponent); would be just fine (in my humble opinion). |
@yyx990803 because the router API choose the actual view, we need only to replace the view, by another component, when a error occur, like in a classic mvc if a entry dont exist pass to other view
|
These days I'm facing the problem again. I did what @yyx990803 suggested to switch But the problem is when clicking back on the login page, it will back to the last second page, because the login page actually isn't in the history stack. I have to hack on the I haven't done yet ,so I don't know whether it will work. |
If the error overlay is meant to disappear by going back in history, then you should definitely push a new entry to the history. It can be the same url, with an |
@fenivana
then use the redirect method to the name 'errors' view. |
I just wanted to say that I took a step back to look at this problem. To me, this isn't a problem with the Vue router. Routes should be immutable that always display a certain component. The only exception is if there is no route to match, in which I feel like the '*' route covers this well. The router is doing its job. It's matching a route to a component. Because your data is bad (404/500 from server) is not the router's fault or problem. The real question (to me) is how best to handle this problem outside of the router. Just my 2c. :) |
@giolf Great idea! |
I don't think it does. Apps will inevitably have URLs that have usernames, ids, and various other identifiers appended to them, and |
@aeharding
But at the same time on the front-end environment we don't have controller. vueJS has great hook (component guard for example) to reach that goal. |
Just for an example of handling the 404s, etc of something, this is how I handle it: Say I have a comment that I can get to at I have a <template>
<component :is="component"></component>
</template>
<script>
import Comment from '@/components/comment/Comment';
import NotFound from './NotFound';
export default {
asyncData({ store, route }) {
return store.dispatch('getCommentById', route.params.id);
},
computed: {
component() {
if (this.$store.getters.currentComment.status === 404) {
return 'NotFound';
}
return 'Comment';
}
},
components: {
Comment,
NotFound
}
};
</script> The above renders very clean markup (no nested divs/components) for easy styling. Everything is just replaced. In essence, I have a Comment component and a Comment page. The Comment page handles rendering either the Comment component -or- the 404 page, or whatever else I want for logic. The This works really well for SSR. I can even set the statusCode of the response on the ssr context in the Comment page. This discussion is probably best outside of github issues. EDIT Just to be clear, I realize this is a bit extra work/scaffolding, but at least it keeps clean HTML and a clean Comment component, which is separate from the Comment page that handles logic for what to do if the request fails. It's been an extremely powerful pattern for me. However, I only really bother doing it when users might be entering a URL/sharing URLs where a 404 would occur. |
so in every
and
where of course will change the main component name in each page, right ? |
@giolf Yes, I do realize it's a bit of scaffolding. I'm not sure how it could be made simpler, but I'd love if someone could make a solution and share it. :) To be fair, the above solution does provide the ability to have a very fine-grained approach to the logic of what to do upon an error. Perhaps you want to have a different 404 page for a specific route - this approach allows that. It's also very testable and debuggable - the router always has a predictable page component that it will render. The page then renders a component via data in the very debuggable vuex store. For some components with For me, I only really need to repeat the above logic for 2 components in a large app. :) I remember the craziness in ui-router, and to me, this is definitely a breath of fresh air! hehe |
@aeharding If you can centralize that logic in a single place and make it a bit more dynamic, every update on that logic could be made only in one place. I'm still not at this point on my SPA. i'm still working on the backend side ... Next week (i hope) i will think about it and i will share also my solution. ;) |
That's good to hear. I look forward to your solution! I guess what I'm trying to get at is that the ability for the view to depart the route and make the view not determinable from the route in ui-router (AngularJS) made for a lot of headaches in large apps. Lots of edge cases... Which is what this issue is proposing with In my opinion, the ability to predict the view given a route is a feature, not a bug. I'd really appreciate a terse solution that keeps the view tied to the route in an immutable way. I'm just not sure how to do that without at least a little custom logic, like I wrote above. Anyways. Sorry for the email spam for anyone subscribed... 🙈 |
Yes im with you about that!
I think in that way you don't repeat anything, yours page component are still the boss that decide if render or not their template or a |
That would definitely work :) I'd definitely like to see an example (I'm a learn-by-example guy), but sounds promising. It would be great to have a page on the router's docs dedicated to this problem at hand, and possible solutions with the associated code. Like the Vue SSR docs. I would love to have this in the hackernews example as well (right now it has basically no handling - renders plaintext "404 - Not Found": https://vue-hn.now.sh/item/thisisnotavalidid). Back in regards to the OP of this issue: I found an image to represent In other words, sure, it could work, but nobody stopped to ask why. :) |
Disclaimer: I'm not really a Front-end or Javascript developer I gave this, in my eyes, a fairly good wack. I had a fairly good shout at a solution similar to what @giolf's proposed two comments above as well as my own solution. The result was a convoluted mess and shenanigans for how the router responds to the next request. The other problem I came across, and perhaps I am doing something wrong, is that when you change a state to display an error component and you call Until I have come up with something better I am sticking with what giolf also proposed above:
But inside my guard the addition of:
This is required to keep the URL the same. The problem with this solution is that Vue adds another browser history entry. In conclusion: Perhaps your computer science professor might not think it's correct, but we really need to be able to do something like this from the navigation guards:
Sorry to bore you with my 2cents and I apologise if I have missed something, I am not an experienced Frontender 👍 and I am having difficulty wrapping my head around this! |
@micbenner The major problem with this is that it makes the route/state relationship non-determinable. Mutating the state matching regardless of the current URL. In other words, for a given route, the page/component cannot be determined from it. (Is it the component that it was initially supposed to be, or is it the NotFoundComponent?) It's also very hard to debug. Extremely confusing in Angular 1 apps I've built with this functionality in ui-router. What is wrong with my solution above, or @giolf's one? |
Hi @aeharding. As far as I can see my concerns with the solution above:
Again I am far from an expert, just trying to find an elegant solution, perhaps you have a better way of avoiding this? At the end of the day though, I think this is a sorely missing feature and quite a glaring hole in the docs. Even if the answer is just an option to direct the route to a named route without changing the URL.
M |
|
Hi, any updates about |
@Dani216 actually nuxt somehow do that, using a error function in the context, you can switch to nuxt(i really recommend) or searching the code |
Since @EduardoRFS mentioned it, I checked out Nuxt.js and came up with this temporary solution based on what Nuxt does. |
@raniesantos your solution is great, but do you have similar solution for nuxt? Nuxt's error method is not the ideal solution, because you can only define one layout for all errors |
I don't quite get why there are so many work arounds that exist for this. I agree with @micbenner on his points that he already made. Creating something external to the router seems to me to be completely out of place. Routing like the following:
This trivially accomplishes the behavior expected where if a route doesn't exist in routing. If there is routing with anything dynamic then From my perspective, it seems that the problem is that the "matching" of a route is unable to be prevented by the user after hooks have begun being called. I believe this would be simple to accomplish by providing a method of rejecting a match via hooks. This would remove the need for "replacing" things at runtime and would instead behave as if it failed to match the route at runtime. The wildcard entry would then behave as expected matching the route on failure. I thought of several methods of accomplishing this but I really think the best would be similar to what @micbenner suggested. I would like to instead propose that it be somewhat less dynamic and be a Since I believe this is a reasonable solution that seems like a small API change. I am sure it is probably a bit of work internally since I am guessing an individual "match" isn't currently able to be rejected after it was "matched". To be honest, if this solution were agreed upon I would be more than willing to work on it myself as it is a feature I very much want. @posva Any thoughts on this suggestion? |
What makes this feature request a bit tricky is that people want to use it for different reasons, some are valid but others we do not completely agree with. |
I see, I appreciate the response~ I do look forward to seeing the implementation or convenient alternative. If bandwidth is ever an issue, as I mentioned, I am more than willing to work on it (especially if I know a PR providing it might be accepted before starting on it). |
I have came up with an improvement of an idea of @aeharding that has less boilerplate. A <template>
<div>
<error-403 v-if="error == 403"></error-403>
<error-404 v-else-if="error == 404"></error-404>
<slot v-else></slot>
</div>
</template>
<script>
import Error403 from "./error-403";
import Error404 from "./error-404";
export default {
props: ['error'],
components: {Error403, Error404},
};
</script> And its usage in another component: <template>
<page-container :error="error">
<h1 v-if="entity">My cool page for {{ entity.id }}</h1>
</page-container>
</template>
<script>
export default {
data(){ return {entity: undefined, error: undefined}; },
mounted() {
this.$http.get('entities/123')
.then(({entity}) => this.entity = entity)
.catch(response => this.error = response.status);
}
};
</script> If everything works, the component is rendering normally but if the request results in an error, the appropriate error page comes in without URL change. Still, the bicycle applies. |
I've encountered a couple of use cases that would be much better solved by either dynamically telling the router which component to render when it matches a route, or by dynamically altering the rendered component with something like
In the past I've resorted to multiple solutions to solve these, but ultimately I think the best solution would be better implemented at the router level since it is already responsible of coordinating these matters. Solving an application matter at the component level doesn't make much sense to be honest. |
I just found out that you can actually use @micbenner solution without side effect he mentioned. Simply add replace option. |
Has anyone figured out a solution to this outside of the The primary reason for keeping the user's URL and displaying a 404 page is to that they can log in and view whatever is they were trying to see. Github does this, for example if you open https://github.com/bbugh/secret-repo in a browser where you're not logged in, the browser keeps the URL in the address bar and you get the login prompts. If you log in, you stay on the same page. |
I think that feature proposed by @fenivana will be useful in a lot of cases. If we could use the router history mode, but selectively mark some routes as abstract, then we could decide which links are meaningfully available as direct links from other websites and which are not. For example, I could like to offer directs links to some features/pages/routes, but prevent direct access to others. At present, the only way I found to implement this behaviour is using the history mode, but using routes only for the links that should be direct, while using v-if to show different components for the others, but in this way I will lose most of the power of the router and end up in very complex nested v-if components. Are there currently other ways to implement this? Otherwise, I think that the possibility to mark only some routes as abstract is absolutely needed. |
Definitely would love |
This is extremely important, 404 pages look awkward without it on SSR. |
Closing to prevent any additional comments repeating what has already been said pinging +20 people 😆 |
router-view
change without changing current URLrouter-view
without changing current URL
The scenario is, when fetching data from backend api failed, I want to route to an error page, but not changing the current location, and prompt the user that you could refresh the page to try again. If the location changed, refreshing the page would always on the error page. Another common usage is showing the login page if user is not logged in, but do not redirect to a login URL.
I know I can achieve it via putting a piece of code into App.vue:
But then these pages can't reuse the layout components that using nested routes.
Proposed API
A prop named
route
for<RouterView>
that allows overriding what is displayed, like in v4 (https://next.router.vuejs.org/api/#route). There are existing tests in https://github.com/vuejs/vue-router-next/tree/master/e2e/modalThe text was updated successfully, but these errors were encountered: