Angular lazy loaded module with child component default route - lazy-loading

Angular 9. Child component is not loaded with lazy loaded module component. Here is my code.
app-routing.module.ts
{
path: '',
redirectTo: '/auth',
pathMatch: 'full'
},
{
path: 'auth',
loadChildren: () => import('./pages/auth/auth.module').then(m => m.AuthModule),
}
auth.module.ts
{
path: '',
component: AuthRootComponent,
children: [
{ path: 'login', component: LoginComponent},
{ path: 'forgot', component: ForgotPasswordComponent},
{ path: '', redirectTo: 'login', pathMatch: 'full' },
]
}
When i use localhost:4200/ it redirects me to localhose:4200/auth. It does not load login component.
But when i hit url in browser (localhost:4200/auth) it will load login component, and new url will be desired url which is localhost:4200/auth/login.
Please tell me, why it does not load login from child array when auth module is loaded and the url is localhost:4200?
URL should be localhost:4200/auth/login but right now getting url localhost:4200/auth

I think you'll have to change from redirectTo: '/auth' to redirectTo: 'auth'. That is because when you're using absolute redirects(e.g /auth), only one redirect operation will be allowed, in this case the one which goes to /auth, meaning that any other redirects(e.g redirectTo: 'login') will be ignored.
Here's where it checks if there were any absolute redirects:
if (allowRedirects && this.allowRedirects) {
return this.expandSegmentAgainstRouteUsingRedirect(
ngModule, segmentGroup, routes, route, paths, outlet);
}
And here is where it states that after an absolute redirect, there can't be any other redirects:
if (e instanceof AbsoluteRedirect) {
// after an absolute redirect we do not apply any more redirects!
this.allowRedirects = false;
// we need to run matching, so we can fetch all lazy-loaded modules
return this.match(e.urlTree);
}
Absolute vs Relative Redirects
Excerpt from my GH repo.
The difference between redirectTo: 'foo/bar' and redirectTo: '/foo/bar' is that:
/ - will start again from the root(the first outermost array of routes)
`` - will start the search from the first route from the array this current route resides in

Related

Dynamically registered route is not working when it is just pushed

Hi i'm facing a problem where i want to add new route dynamically
My route structure will look something like this
I'm using "vue-router": "^3.5.3"
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, name:'User'
children: [
{
path: 'profile', component: Profile, name:'Profile'
children: [
{
path: 'about', component: About, name:'About'
children: [
{
path: 'details', component: Details, name: 'Details'
}
]
}
]
}
]
}
]
})
Now my intention is to add child route to Details
this is how my pseudo code looks like
findRouteWithName(routeObj,routeName)
{
// recursive find with any nested level
....
return foundRoute;
}
let routeObj = findRouteWithName(this.$router.options.routes,'Details');
routeObj.children.push(Route object{});
//this.$router.addRoutes([routeObj]) // this line creates problem as mentioned below in **problem:**
Note: if i'm doing like this https://stackoverflow.com/a/48833074/10054910 it is creating multiple nested routes in the url
Problem: page is becoming blank with direct push on children even route does not change in url. with this https://stackoverflow.com/a/48833074/10054910 approach route changes in url but append 2 times
Please help me thanks in advance !!

Vue Router - catch all wildcard not working

I'm using Vue Router with Vue 3 and am trying to add a catch-all route to redirect the user if they try and access an invalid URL. When I try and use the wildcard (*), i get the following error logged to the console:
Uncaught Error: A non-empty path must start with "/"
at tokenizePath (vue-router.esm.js?8c4f:975)
at createRouteRecordMatcher (vue-router.esm.js?8c4f:1106)
at addRoute (vue-router.esm.js?8c4f:1190)
at eval (vue-router.esm.js?8c4f:1335)
at Array.forEach (<anonymous>)
at createRouterMatcher (vue-router.esm.js?8c4f:1335)
at createRouter (vue-router.esm.js?8c4f:2064)
at eval (index.js?a18c:26)
at Module../src/router/index.js (app.js:1402)
at __webpack_require__ (app.js:854)
I'm assuming this is because I don't prepend the path containing the asterisk with a '/', but if I do this then the catch all doesn't work. Here are my routes:
imports...
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/user',
name: 'User',
// route level code-splitting
// this generates a separate chunk (user.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "user" */ '../views/user/User.vue'),
children: [{path: '', component: UserStart}, {path: ':id', component: UserDetail}, {path: ':id/edit', component: UserEdit, name: 'userEdit'}]
},
{path: '/redirect-me', redirect: '/user'},
{path: '*', redirect: '/'}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
The wildcard route is the last object in the routes array. Does anyone know what I'm doing wrong?
Catch all routes (/*) must now be defined using a parameter with a custom regex: /:catchAll(.*)
For example:
{
// path: "*",
path: "/:catchAll(.*)",
name: "NotFound",
component: PageNotFound,
meta: {
requiresAuth: false
}
}
Personally, for Vue 2's * (star or catch all) routes in Vue 3 I use:
{
path: '/:pathMatch(.*)*', <== THIS
name: 'not-found',
component: NotFound
}
Catch all routes (*, /*) must now be defined using a parameter with a custom regex:
The parameter name can be whatever you want like catchAll, pathMatch, noPage etc
{
path: '/:pathMatch(.*)*', //will match everything and put it under `$route.params.pathMatch`
name: 'not-found',
component: NotFound
}
{
path: '/user-:afterUser(.*)',// will match anything starting with `/user-` and put it under `$route.params.afterUser`
component: UserGeneric
}
/:pathMatch(.*)*
The last * it is necessary if you plan on directly navigating to the not-found route using its name.
If you omit it the / character in params, it will be encoded when resolving or pushing.
For example if you use path: /:pathMatch(.*) (note: without the last asterisk) and you go to /user/not-found (a page that doesn't exists) the this.$route.params.pathMatch will be a string => 'user/not-found'
// bad example if using named routes:
router.resolve({
name: 'bad-not-found',
params: { pathMatch: 'not/found' },
}).href // '/not%2Ffound'
Instead, if you use path: /:pathMatch(.*)* (note: with asterisk) this.$route.params.pathMatch will be an array ['user', 'not-found']
// good example:
router.resolve({
name: 'not-found',
params: { pathMatch: ['not', 'found'] },
}).href // '/not/found'
Please read docs: From migration from vue 2 to vue 3 and Catch all / 404 Not found Route

Use same component with multiple routes, depending on path component type

I am trying to use one component for multiple routes, but depending on the type of the path component a different action has to be performed. I have three main views:
LevelOneView: This is the landing page, and only shows a search widget. Once the query has been setup, it redirects to the /search path and stores the query in VueX
LevelTwoView: This view shows the result of the query. Once the user clicks on the result, it brings them to the /:id path
LevelThreeView: This view shows the details of the listing
These three views need to share some of the paths. The idea is the following:
/search => Go to LevelTwoView, no special work
/:city => A location (london, newyork, ...) can be inserted and it should go to the LevelTwoView with the query already set to search only listings available in that region. The url should be /search
/:id => Go to LevelThreeView using the id as a parameter for the fetch req. This is a number
I set up a beforeEnter guard on the /:city route so that it checks if the path param is a number, and in that case it should redirect to the LevelThreeView. This works fine if I am going from the /search path to the /:id (or even directly to the /:id path from the outside), but if I am going from a LevelThreeView to a LevelThreeView it does not work and returns this error:
Uncaught (in promise) undefined
eval # vue-router.esm.js?8c4f:2051
abort # vue-router.esm.js?8c4f:2082
eval # vue-router.esm.js?8c4f:2131
beforeEnter # router.js?41cb:43
iterator # vue-router.esm.js?8c4f:2120
step # vue-router.esm.js?8c4f:1846
eval # vue-router.esm.js?8c4f:1847
eval # vue-router.esm.js?8c4f:2139
eval # main.js?56d7:115
iterator # vue-router.esm.js?8c4f:2120
step # vue-router.esm.js?8c4f:1846
step # vue-router.esm.js?8c4f:1850
runQueue # vue-router.esm.js?8c4f:1854
confirmTransition # vue-router.esm.js?8c4f:2147
transitionTo # vue-router.esm.js?8c4f:2034
push # vue-router.esm.js?8c4f:2365
eval # vue-router.esm.js?8c4f:2779
push # vue-router.esm.js?8c4f:2778
goToListing # ListingCard.vue?6c5e:147
click # ListingCard.vue?917d:18
invokeWithErrorHandling # vue.runtime.esm.js?2b0e:1854
invoker # vue.runtime.esm.js?2b0e:2179
original._wrapper # vue.runtime.esm.js?2b0e:6911
I set up the router in the following way:
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'LevelOne',
component: LevelOneView,
beforeEnter: (to, from, next) => {
// Clear the query
store.commit('resetQuery');
return next();
}
},
{
path: '/search',
name: 'LevelTwo',
component: LevelTwoView
},
{
path: '/:city',
name: 'LevelTwoWithCity',
component: LevelTwoView,
beforeEnter(to, _, next) {
// Strip the leading slash
let toPath = to.path.replace("/", "").toLowerCase();
// Check if the path is a number and push the lv3 view
if (!isNaN(toPath)) {
console.log("Router: found valid path for lv 3")
const id = parseInt(toPath);
console.log(id);
return next({name: 'LevelThree', params: { id: id }});
}
// Load up the query
let query = store.getters.getQuery;
// Get the name of all the regions, as a list, to lower case
let regions = query.regions.map(el => el.name.toLowerCase());
// Check if we have a matching one
if (regions.includes(toPath)) {
// First, deactivate them all
query.regions.map(el => el.value = false)
// Then, activate only the one that matched
query.regions.filter(el => el.name.toLowerCase().includes(toPath)).map(el => el.value = true);
// Commit the query
store.commit('setQuery', query);
}
return next();
}
},
{
path: '/:id',
name: 'LevelThree',
component: LevelThreeView,
beforeEnter(to, from, next) {
console.log("Inside Level3 beforeEnter");
next();
}
},
{
path: '/not-found',
name: '404',
component: NotFound,
},
{
path: '*',
name: '404',
component: NotFound,
},
]
})
I am a beginner at this so I might be making a trivial mistake. If anyone can give me any pointers it would be greatly appreciated.
Thanks!
In the end, I have been able to find a "workaround". For each of the routes, you can define aliases. So I changed the code to use them, and load up all the possible routes at start up:
// Generate the aliases
let cities = myCities.map(el => "/" + el.name.toLowerCase());
// Get the base case (i.e. the first element to have something to match against)
let baseCity = cities.splice(0, 1)[0];
export default new Router({
...
routes: [
{
path: '/',
...
},
{
path: '/search',
...
},
{
path: baseCity,
name: 'LevelTwoWithCity',
component: LevelTwoView,
alias: cities,
... // Here I set up the query object to be passed on
},
{
path: '/:id',
name: 'LevelThree',
component: LevelThreeView
},
{
path: '*',
name: '404',
component: NotFound,
},
]
})
in theory, this could be implemented dinamically using the addRoutes() method described here

Angular 5, Route not working in layout, whereas parent is working

routing in layout module:-
{ path: 'userlogin', component: UserloginComponent }
parent routing in app.routing
{
path: 'login',
component: UserLayoutComponent,
pathMatch: 'full',
}
whenever i call it with /userlogin this url, it every time gives error saying that this Cannot match any routes. URL Segment: 'userlogin'

Vue ,listen to route param change but not child route change?

I have the following structure in vue.js ,using vue-router.
routes: [
{
path: '/domains',
component: ListOfDomains
},
{
path: '/domain/:domainName',
component: Domain,
children: [{
// Photos Tab content will be rendered inside domain's <router-view>
// when /domain/:id/photos is matched
path: 'posts',
name: 'posts',
component: PostsTabContent
},
{
path: 'photos',
name: 'photos',
component:PhotosTabContent
}
]
}
]
In my Domain component I use a watcher to fetch domain related data from server such as:
"$route": function(to, from) {
console.log("watcher triggered");
//do update only if domain has changed
//do something , ajax request , update store etc
}
This seems to work fine, but my problem is that I only want to do my updates if the url changes from, say, domain/domain23/posts to domain/domain45/posts and not when it changes from domain/domain23/posts to domain/domain23/photos.
How can I watch only that level in the route for changes?