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

Remove existing routes #1234

Open
alekbarszczewski opened this issue Mar 9, 2017 · 39 comments
Open

Remove existing routes #1234

alekbarszczewski opened this issue Mar 9, 2017 · 39 comments

Comments

@alekbarszczewski
Copy link

@alekbarszczewski alekbarszczewski commented Mar 9, 2017

Right now it is possible to dynamically add routes by calling router.addRoutes([/* routes */]). It would be nice if it was possible to also remove/replace routes on the fly. In my app user may define routes by himself (on the fly). I keep user configuration in the Vuex store, and when it is updated I want to completely replace whole router routes configuration (though replacing/removing single route would be also handful but I guess then it should also handle removing/replacing/adding child routes instead of only dealing with top-level routes, which would be quite complicated). I imagine that after replacing routes router would check if current path matches any of the new routes and navigated to it.

It could be for example method router.replaceRoutes([/* routes */]) or router.setRoutes (in addition to method router.addRoutes which already exists).

Please note that this situation can be handled without VueRouter by setting one global route /* that will match every possible path, performing route matching inside this global route component (based on dynamic config stored in Vuex store for example) and dynamic rendering of component(s) that matched current path. However this is very ugly solution which looses all benefits of VueRouter, and handling child routes would be complicated.

In fact I wanted to handle it by patching VueRouter manually, but it seems that createMatcher (https://github.com/vuejs/vue-router/blob/dev/src/create-matcher.js#L16) does not expose pathMap and nameMap so it is not possible modify routes configuration in other way than by calling router.addRoutes (I guess it's intentional :).

@posva
Copy link
Member

@posva posva commented Mar 9, 2017

Why would you ever need to delete a created route without adding a new one replacing it?
I think you may be interested in #1129

@alekbarszczewski
Copy link
Author

@alekbarszczewski alekbarszczewski commented Mar 9, 2017

In my app user may dynamically set routing. For example he has three predefined components A, B and C; In the application (in browser :) he can manage route configuration; let's say there is a textarea with following JSON:

[{ path: "/my-custom-path", component: "A" }, { path: "/some/other/path/:id", component: "B" }, ...]

For now I can just add new routes to the router:

import A from '@/components/A';
import B from '@/components/B';
import C from '@/components/C';

const allComponents = { A, B, C }; 

// "routes" is array of routes provided by user/client in textarea (textarea is just an example :).
onConfigChanged = routes => {
  routes = routes.map(route => {
    return { path: route.path, component: allComponents[route.component] };
  });
  router.addRoutes(routes);
};

// onConfigChanged is called on each change to configuration by user/client

What I need is to completely replace all routes: router.replaceRoutes(routes) or maybe router.addRoutes(routes, { replace: true }) (it should completely replace routes configuration).

#1129 is close but it won't solve my problem because I don't know route names...

I know that this is not primary use-case for VueRouter, but it would allow to do more advanced things (like my use-case).

@ianaya89
Copy link

@ianaya89 ianaya89 commented Mar 17, 2017

I like this feature request, I have uses cases where I need to replace url parameters dynamically just to generate a URL to share and grant a unique access for a view with data that I already have in memory (where I don't need view changes or async operations).

@nickforddesign
Copy link

@nickforddesign nickforddesign commented Apr 12, 2017

My application has different very routes for different user roles (some are replaced, some should be inaccessible or not exist at all). While it is already possible to achieve this using hooks and basic permissions definitions, I am interested in the idea of replacing the routes entirely on login/logout. The set of routes available could then be unique for each user role, and public/non-authenticated users.

@fritx
Copy link

@fritx fritx commented Jul 1, 2017

I had my components depends on the api, and the api now needs to depend on the router,
however, the router has to depend on the components,
because of the new Router({ routes: [{ component }] }) syntax,
which causes a circular require now.

(the resolve => reuqire([path], resolve) syntax doesn't work for me.)

+1 for dynamic routes config, so I can export the router instance first.

alekbarszczewski added a commit to alekbarszczewski/vue-router that referenced this issue Jul 14, 2017
'replaceRoutes' method replaces all routes in router

close vuejs#1234
@vuejs vuejs deleted a comment from lerit Sep 18, 2017
@vuejs vuejs deleted a comment from Telanx Sep 18, 2017
@vuejs vuejs deleted a comment from fancyboynet Sep 18, 2017
@nickforddesign
Copy link

@nickforddesign nickforddesign commented Sep 26, 2017

Is there any intention of implementing this? It felt like there was some momentum on this for a while

@posva posva removed the 2.x label Oct 8, 2017
@frenchbread
Copy link

@frenchbread frenchbread commented Dec 1, 2017

My use case: Modular app, where you enable/disable modules (basically separate components, pages) that have pre-configured routes. Adding a route after enabling a module is ok, but when disabling module, previous routes need to be removed.

@goldenram
Copy link

@goldenram goldenram commented Dec 7, 2017

I agree this is needed. Imagine you implement a landing page that loads various apps dynamically, so that each app loaded would want to clear any previously loaded routes and load its own in. Also imagine that this information is sent up from the server dynamically.

This seems like a simple api of router.removeRoutes ([routeKeys]) or empty array to remove all.

@vuejs vuejs deleted a comment from frenchbread Dec 7, 2017
@khaledh
Copy link

@khaledh khaledh commented Dec 28, 2017

The case for authorization-based (or capability-based) route definitions seems to be very common for sufficiently sophisticated apps.

In my case (somewhat similar to @nickforddesign's), I'd love to be able to start with a minimal set of public routes for unauthenticated users, and if they try to access any of the protected routes they get a 404 (or maybe redirected to login). Upon login, the server would respond with a list of accessible routes based on the current user role and/or app configuration.

@JounQin
Copy link
Contributor

@JounQin JounQin commented Dec 29, 2017

All interceptors and authentications can be implemented with beforeEach hook, I still don't understand why you need add or replace routes dynamic.

@silkentrance
Copy link

@silkentrance silkentrance commented Dec 29, 2017

@JounQin Please consider a client side plugin system that allows individual plugins to be disabled/enabled. When enabled, the routes provided by the plugin must be added to the system. When disabled, these routes need to be removed again in order for the application to fail consistently, namely with a 404 instead of continuing to render the components/views associated with that route.

An alternative, of course, would be to implement a beforeEnter hook which checks whether the route is available and redirecting to 404 instead when it is not.

@ghost
Copy link

@ghost ghost commented Jan 3, 2018

@posva

Why would you ever need to delete a created route without adding a new one replacing it?
I think you may be interested in #1129

How could you implement this? Is it possible to create a new Router and assign it to the main app Router?

@coxy
Copy link

@coxy coxy commented Jan 16, 2018

@limoli I had the same issue and solved it by replacing the router's matcher object with one from a newly created router instance.. something like:

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

const createRouter = () => new Router({
  mode: 'history',
  routes: []
})

const router = createRouter()

export function resetRouter () {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // the relevant part
}

export default router
@xon52
Copy link

@xon52 xon52 commented Jan 24, 2018

I'm really keen on having this feature. My app dynamically loads/unloads content, so if I unload a module, it'd be fantastic if it's be easy to unlink the routes (and redirect to my 404 or home page).
Edit: Router guards can work, but this would be much easier because I wouldn't need to have like 20 guards in place to accommodate all my separate modules... Oh, and I don't think we can dynamically add router guards?

@JesseZhao1990
Copy link

@JesseZhao1990 JesseZhao1990 commented Mar 15, 2018

@coxy great~ it works~

@ale-grosselle
Copy link

@ale-grosselle ale-grosselle commented May 4, 2018

I have similar problem: I have many minis app with its routes: Content app, User app.
A main app, used and load these mini apps.
Main app load mini app asynchronously.

My goal is:

  1. User wants to go "content/details/video_1"
  2. Main app seeing the hash, knows that users wants to use mini app "Content". So, mini app "Content" and its routes are loaded?
  3. If no, ok loaded component, apply routes and finally show the component

Below my code:
MAIN APP Router code:

import Vue from "vue";
import Router from "vue-router";
import HelloWorld from "@/components/HelloWorld";

Vue.use(Router);

const loadSection = function(to, next) {
	//Load Content section:
	if (to.includes("/content")) {
		let promiseContent = import("dynamicImports/Content");
		promiseContent.then(
			(contentItem) => {
				contentItem = contentItem.default;
				//call init mini app (add routes mini app):
				contentItem.init(router);
				//After apply mini app routes, i must to force next(to)
                                //instead of next(). next() do nothing... bug???
                                 next(to);
			}
		);
	} else {
		next();
	}
};


const router = new Router({
	routes: [
		{
			path: "/",
			name: "HelloWorld",
			component: HelloWorld
		}
	]
});

router.beforeEach((to, from, next) => {
	if (router.match(to.path).matched.length === 0) {
		loadSection(to.path, next);
	} else {
		next();
	}
});

export default router;

MINI APPS EXPORT (dynamicImports/Content):

import RouterConfig from "router";
import MainApp from "components/app.vue";
import ContentDet from "components/contentDetails/ContentDetails.vue";

export const App = {
	component: MainApp,
	init: function(router:any) {
		//Add routes:
		router.addRoutes(RouterConfig);
	}
};

what do you think of this solution?

lbwa added a commit to lbwa/adminize-template that referenced this issue Aug 11, 2019
to replace router matcher to implement dynamic routes deletion
vuejs/vue-router#1234 (comment)
@Aaron3
Copy link

@Aaron3 Aaron3 commented Aug 30, 2019

Here's a very dirty function to append new child routes. I'm hoping the dirtiness of it encourages the maintainers to come up with a better solution. Keep in mind the console.warn suppresses ALL warnings when adding the routes and that to handle all cases you'll need an additional if statement or two. This has some clear problems if you try to update a named route because vue-router refuses to overwrite the named route reference 🤦‍♂️; but, adding the same named route multiple times doesn't seem to cause any problems.

My use case was entirely appending new child routes to a parent route that handled global data preloading and authentication based on authenticated user's permissions.

I can neither confirm nor deny whether or not this was put into production. 😟

export default function appendRoutes(router, newRoutes) {
  const existingRoutes = router.options.routes;

  newRoutes.forEach( (newRoute) => {
    // try to find an existing route based on the path
    let existingRoute = existingRoutes.find(r => r.path === newRoute.path);

    // if the specific route path already exists, then try to merge the children
    if(existingRoute && newRoute.children && newRoute.children.length) {
      // ensure type
      existingRoute.children = existingRoute.children || [];
      // append the new children
      existingRoute.children.push(...newRoute.children);

      // suppress duplicate route warnings to quiet logs during development
      const warn = console.warn;
      console.warn = () => {};
      router.addRoutes([existingRoute])
      console.warn = warn;
    }else{
      router.addRoutes([moduleRoute])
    }
  })
}
@riri
Copy link

@riri riri commented Sep 18, 2019

I give another usage example of deleting/replacing routes.
In an app, I use routes to switch pages, all with the same component, changing content.
Pages are loaded from an api so routes can change (added/modified/deleted) because the user can change the page name that can be seen in the URL thanks to router.

So the flow is the following :

  1. app startup, only login and register routes are available if there is no authentication yet
  2. when authenticated, pages are loaded from rest service
  3. I rebuild routes based on available pages, plus the static ones (logout, login, etc...)
    4a) user creates a new page (so a new route)
    4b) user deletes a page (so route should not be available anymore
    4c) user changes page name, equals deleting the route with old name, and adding a new one
  4. user has a default page, / route redirects to it and of course, they can change their default page :)
@dannynpham
Copy link

@dannynpham dannynpham commented Oct 11, 2019

My use case is a user can create, edit, and delete routes with configurations for a configurable component that would come in through the API and would be added at runtime. When I add, remove, reorder routes I get the duplication routes warning.

@liuanxin
Copy link

@liuanxin liuanxin commented Dec 4, 2019

I don't understand why router.matcher = createRouter().matcher would have so many people agree, at least I did not succeed. vue 2.6.10, vue-router 3.1.2

And I tried to add a few more

const reset = () => {
  router.matcher = createRouter().matcher

  // Here's how to try it out

  router.addRoutes([...])

  // or

  router.options.routes = [...]
}

Why is there only one official addRoutes([]), even if it gives one more clear(), otherwise there will be a lot of duplicate routing warnings when adding?

The result is still not effective, but I feel that My problem this should be caused by the following problem.

asterisk(*) must be placed at the end

Generally, the initial router has * => 404. As a result, the route added by the subsequent router cannot be routed at all, so it can be solved by refreshing the page and creating again.

@lugging
Copy link

@lugging lugging commented Jan 13, 2020

this is my code , success!!!

export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
router.options.routes = []
}

@Jiny3213
Copy link

@Jiny3213 Jiny3213 commented Jan 18, 2020

fail to use reset method with "router.matcher = newRouter.matcher" like @coxy
I lost all my router after reset, it seem dont't work :(

@dmitry
Copy link

@dmitry dmitry commented Feb 4, 2020

So @coxy method doesn't work anymore in the latest vue-router?

PS. I just tried - it works fine.

@brikoleur
Copy link

@brikoleur brikoleur commented Feb 17, 2020

My apps are also authorisation-based and I would want to be able to enable/disable routes depending on signed-in/signed-out state. I will also be working on an app where modules are dynamically enabled or disabled, which means routes to them will need to be switched on and off for them as well.

I can find workarounds but in my view this should be solved in the router itself. It feels strange that it's only possible to add routes, never remove or rewrite them.

@davispuh
Copy link

@davispuh davispuh commented Apr 11, 2020

How this is fixed in 4.0? I don't see it implemented anywhere.

I'm also looking for a way to remove existing route and replace route functionality.

@rathga
Copy link

@rathga rathga commented Apr 20, 2020

Also looking for this feature. Developing an app where the user and enable/disable modules, and each module has its own routes. Ideally when unloading a module the related routes should be disabled (so user goes to 404 instead of a non-functional component if they try to access it).

If we already have addRoutes() surely adding a clear() or a remove() should be a trivial new feature to add.. ?

@PLQin
Copy link

@PLQin PLQin commented Apr 28, 2020

Why do you need a delete routing feature?
Shouldn't most privileged routes be configured by the server?

your router.js may only have this code

// router.js
{
  path: '/login',
  component: () => import('@/views/login/index')
},
{
  path: '/register',
  component: () => import('@/views/register/index')
},
{
  path: '/404',
  component: () => import('@/views/error-page/404')
},
{
  path: '*',
  redirect: '/404'
}

When a user logs into your web application with an account and password, he or she immediately requests a menu data from the server, which you then consolidate into your local router area using addRouter :

import router from './router.js'

// after the server requests the menu data based on the current user information
// `accessRoutes ` It's an array , It's an routers
router.addRoutes(accessRoutes)
@brikoleur
Copy link

@brikoleur brikoleur commented Apr 28, 2020

My users also logout. I need to reset the routes to the logged-out state after that.

I have another use case where users can create and delete objects which map to routes in the UI. The server also notifies the UI if another user in the same group has made these types of changes. I.e. the routes need to be dynamically updated through user actions.

@jaumebalust
Copy link

@jaumebalust jaumebalust commented May 12, 2020

Hi,
I have solved how to check if a route is from a different project in the same host without the need of replacing routes (the routes of the projects don't overlap).

It consists on checking the path on the url against the routes of the router.

In the App.vue:

      mounted(){
          this.checkIfWeComeFromDifferentProject()
      },

      methods:{
        checkIfWeComeFromDifferentProject(){
            if(this.checkRouteOfThisProject()){
                this.confirmLogout() //here is where you logout the user manually instead of having them do it
            }
        },
        getRouteCorePath (route) {
            route = route.slice(1)// remove first slash "/"
            //remove /:id from pages like "/vehicles/1"
            let indexOfSlash = route.indexOf('/')
            if (indexOfSlash > 0) {
                route = route.substr(0, indexOfSlash)
            }
            return route
        },
        checkRouteOfThisProject(){
            var This = this
            var found = this.$router.options.routes.find(route=>{
                var iteratedPathToCheck = This.getRouteCorePath(route.path)
                var currentRouterPathToCheck = This.getRouteCorePath(This.$route.path)
                return iteratedPathToCheck == currentRouterPathToCheck
            })
            return !found
        },
      },

    watch:{
        $route (to, from){
            this.checkIfWeComeFromDifferentProject()
        }
    }

Cheers,

@aparajita

This comment has been hidden.

@renatodeleao
Copy link

@renatodeleao renatodeleao commented Nov 12, 2020

Another usecase: oversimplifying a lot for demo purposes but let's say that I have basically 2 apps running while migrating a codebase: legacy(not-vue) to next (vue) and they both map the same urls for user convenience while sharing links in this transition period. So myapp.com/some-page will render either a legacy app view or a next app view based on a user featureFlag (like a beta testing, but remember they use the same url!!) so picture an incremental migration.

// inital router.js
const routes = [...alreadyMigratedNextRoutes] // already battle-tested vue routes no longer under the featureFlag, no more legacy views fallback
const router = new VueRouter({ routes })

// some data fetching work...

if (store.state.curAccount.featureFlag) {
  router.addRoutes(underTestingNextRoutes) // the ones with the same paths as legacy views, now curAccount will get `next` views
} 

// else do nothing (as in navigating to those paths will load the `legacy` app versions
// voilá this works

The problem is that switching accounts on the app (meaning no page reload) and assuming curAccount had featureFlag === true and we switch to account_b has the featureFlag === false, means that I need to remove underTestingNextRoutes from the router routes array so that account_b effectively sees the legacy app view when navigating to that url.

// pseudoCode for some selectbox

onSwitchAccounts(targetAccount) {
  this.$store.dispatch('fetchAccount', targetAccount).then((accountBPayloadj) => {
      this.$store.commit('UPDATE_CUR_ACCOUNT', accountBPayload)
       
      if (accountBPayload.featureFlag === false) {
         // yup would be handy
         this.$router.removeRoutes(underTestingNextRoutes)

        // Now if accountB goes to `myapp.com/some-page`, it should see the `legacy` version.
      }
   })
}
 

Does this make sense on why I can't simply use router hook/guard? In practice i want to redirect... to the same url.

Note that i'm not saying there isn't another way of doing it, and yes "you should just use a next.myapp.com" are expected (reddit) counter-arguments. I'm just trying to express what it seems to be a practical use-case for the feature.

✌️ ☮️

@redfox05
Copy link

@redfox05 redfox05 commented Nov 25, 2020

@posva It says you added fixed in 4.x but can you link to a commit/documentation so we can know how to use it and close this issue if it solves it?

@posva
Copy link
Member

@posva posva commented Nov 25, 2020

@vuejs vuejs deleted a comment from xyfy Nov 25, 2020
@redfox05
Copy link

@redfox05 redfox05 commented Nov 26, 2020

@posva Thanks, was hoping for a commit hash or link to documentation, but understand you're probably busy.

For others interested in the information, I managed to find the docs for it here: https://next.router.vuejs.org/guide/advanced/dynamic-routing.html#removing-routes

I was surprised this issue was not linked to a commit when the feature was implemented, and this issue is still Open, so figured it was not fully complete? Hence I tried looking for a commit hash but couldn't find one. Hopefully the docs will be enough for people.

@posva posva changed the title Feature request: replace routes dynamically Remove existing routes Dec 29, 2020
@posva posva added this to To Do in v3 pre v4 Jan 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Longterm
Design proposals + discussion (high p...
Linked pull requests

Successfully merging a pull request may close this issue.