Angular router Navigate not working with lazy loading - lazy-loading

In my Angular 13 Ionic 6 I am attempting to navigate to the "home" page.
Running on Android.
Here's the component code (signup) that is calling the navigation (abbreviated):
import { Component, OnInit, NgZone } from '#angular/core';
import { Router } from '#angular/router';
constructor(
private route: Router,
private zone: NgZone,
) { }
routeBasedOnRole(role: number) {
console.log('Routing based on role: ', role);
this.route
.routeReuseStrategy
.shouldReuseRoute = function () {
return false;
};
if (role==2) {
//Helper
this.zone.run(() => {
this.route.navigate(['/my-invitations']);
})
} else {
//Learner or both
console.log('Routing to home now');
this.zone.run(() => {
this.route.navigate(['/home'])
.then(nav => {
console.log(nav); // Always returns true
}, err => {
console.log(err)
});
})
}
}
In the app-routing.module.ts routes array, for the 'home' path, when I route to the:
component: HomePage
The routing is executed OK.
However, when I lazy load the route:
loadChildren: () => import('./home/home.module').then( m => m.HomePageModule)
...the screen does not route - it stays in the calling component.
The route.navigate() promise always returns true regardless.
Here's the app-routing.module.ts:
import { NgModule, Component } from '#angular/core';
import { CommonModule } from '#angular/common';
import { PreloadAllModules, RouterModule, Routes } from '#angular/router';
import { HomePage } from './home/home.page';
import { SignupPage } from './signup/signup.page';
import { HelpersPage } from './helpers/helpers.page';
import { AddHelperPage } from './helpers/add-helper/add-helper.page';
import { MyInvitationsPage } from './my-invitations/my-invitations.page';
import { InvitationsResponsesPage } from './invitations-responses/invitations-responses.page';
import { ChatPage } from './chat/chat.page';
import { SettingsPage } from './settings/settings.page';
import { FeedbackPage } from './feedback/feedback.page';
const routes: Routes = [
{
path: 'home',
//loadChildren: () => import('./home/home.module').then( m => m.HomePageModule)
component: HomePage
},
{
path: 'signup',
loadChildren: () => import('./signup/signup.module').then( m => m.SignupPageModule)
//component: SignupPage
},
{
path: 'helpers',
loadChildren: () => import('./helpers/helpers.module').then( m => m.HelpersPageModule)
},
{
path: 'add-helper',
loadChildren: () => import('./helpers/add-helper/add-helper.module').then( m => m.AddHelperPageModule)
},
{
path: 'my-invitations',
loadChildren: () => import('./my-invitations/my-invitations.module').then( m => m.MyInvitationsPageModule)
},
{
path: 'chat',
loadChildren: () => import('./chat/chat.module').then( m => m.ChatPageModule)
},
{
path: 'invitations-responses',
loadChildren: () => import('./invitations-responses/invitations-responses.module').then( m => m.InvitationsResponsesPageModule)
},
{
path: 'settings',
loadChildren: () => import('./settings/settings.module').then( m => m.SettingsPageModule)
},
{
path: 'feedback',
loadChildren: () => import('./feedback/feedback.module').then( m => m.FeedbackPageModule)
},
];
#NgModule({
declarations: [
HomePage,
SignupPage,
HelpersPage,
AddHelperPage,
MyInvitationsPage,
InvitationsResponsesPage,
ChatPage,
SettingsPage
],
imports: [
CommonModule,
RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
],
exports: [RouterModule]
})
export class AppRoutingModule { }
Btw, the signup page that is calling for the route, is itself lazy-loaded OK from the app.component using the same route.navigate() call. It seems that maybe subsequent routings to lazy-loaded pages are not executing?
Have looked through many similar questions - to no avail.
What am I missing?

Related

Vue 3, SocketIO - won't join the room after login and router push

I am experiencing the following issue - once the user is logged in, and onMounted event is finished, SocketIO client side should join the room. But this doesn't happen for some reason. I have to manually refresh browser in order to join the room. What am I doing wrong here?
I have the following code for the SocketIO on the client side:
import { io } from "socket.io-client";
const token = window.localStorage.getItem('TOKEN') || window.sessionStorage.getItem('TOKEN')
console.log(token);
const ioSocket = io('https://dolphin-app-e4ozn.ondigitalocean.app', {
withCredentials: true,
transportOptions: {
polling: {
extraHeaders: {
'authorization': `${token}`,
},
},
},
});
export const socket = ioSocket
The vue 3 router logic:
import { createRouter, createWebHistory } from 'vue-router'
import Landing from '#/views/Landing.vue'
import Login from '#/views/login/Login.vue'
import ResetPassword from '#/views/login/ResetPassword.vue'
import ForgotPassword from '#/views/login/ForgotPassword.vue'
const routes = [
{
path: '/login',
name: 'login',
component: Login,
meta: {
isGuest: true,
title: 'Servant | Login'
}
},
{
path: '/resetPassword',
name: 'resetPassword',
component: ResetPassword,
meta: {
isGuest: true,
title: 'Servant | Reset Password'
}
},
{
path: '/forgotPassword',
name: 'forgotPassword',
component: ForgotPassword,
meta: {
isGuest: true,
title: 'Servant | Forgot Password'
}
},
{
path: '/',
name: 'landing',
component: Landing,
meta: {
requiresAuth: true,
title: 'Servant',
role: 'waiter'
}
},
{
path: '/:pathMatch(.*)*',
component: Landing
},
]
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior() {
// always scroll to top
return { top: 0 }
},
})
router.beforeEach((to, from, next) => {
document.title = to.meta.title || "Servant"
let token = window.localStorage.getItem('TOKEN') || window.sessionStorage.getItem('TOKEN')
if(to.meta.requiresAuth && !token)
{
next({name: 'login'})
}
if (token && to.meta.isGuest )
{
next({ name: 'landing' })
}
next();
});
export default router
Login component logic:
function login() {
loading.value = true
formClass = ''
if (user.remember)
{
window.localStorage.setItem('remember', user.remember)
}
mainStore
.login(user)
.then((response) => {
loading.value = false
router.push({
name: 'landing',
})
})
.catch((err) => {
loading.value = false
errorMsg.value = err.response.data.messages.error
formClass = 'was-validated'
})
}
Once the component is mounter I have following logic:
onMounted(() => {
socket.emit("join", import.meta.env.VITE_SOCKET_ROOM, (message) => {
console.log(message);
});
})
On the SocketIO server side I have following logic:
io.use((socket, next) => {
const header = socket.handshake.headers["authorization"];
if(header !== 'null')
{
jwtVerify(header, secret).then((res) => {
if (res === true) {
const jwt = jwtDecode(header);
servantID = jwt.payload.iss;
return next();
}
return next(new Error("authentication error"));
});
}
});

Vue router i18n redirecting to duplicate locale in route

I Have a vue application with i18n and authentication via naviation guard.
My issue is that when im running:
#click="pushRouteTo(`${$i18n.locale}/list/` + f.id)"
pushRouteTo(route) {
try {
this.$router.push(route);
} catch (error) {
if (!(error instanceof NavigationDuplicated)) {
throw error;
}
}
}
I am getting pushed to example.com/en/en/list/123 instead of example.com/en/list/123
When i place a debugger in my navigation guard it says that my to.path is "/en/en/list/123"
but i am pushing /en/list/123. How can this be?
Does it have something to do with my redirect in my router?
Here is my router.js:
import Vue from 'vue';
import Router from 'vue-router';
import Home2 from './views/Home2.vue';
import Login from './views/Login.vue';
import Register from './views/Register.vue';
import ErrorLanding from './views/ErrorLanding.vue'
import Root from "./Root"
import i18n, { loadLocaleMessagesAsync } from "#/i18n"
import {
setDocumentLang
} from "#/util/i18n/document"
Vue.use(Router);
const { locale } = i18n
export const router = new Router({
mode: 'history',
base: '/',
routes: [
{
path: '/',
redirect: locale,
meta: {authRequired: false},
},
{
path: "/:locale",
component: Root,
meta: {authRequired: false},
children: [
{
name: 'Home',
path: '',
component: Home2,
meta: {authRequired: false},
},
{
name: 'Login',
path: 'login',
component: Login,
},
{
path: 'register',
component: Register,
},
{
path: 'lockedpage',
name: 'lockedpage',
webpackChunkName: "lockedpage",
meta: {authRequired: true},
component: () => import('./views/LockedPage.vue')
},
{
path: '*',
component: ErrorLanding,
name: 'NotFound'
}
]
}
],
router.beforeEach((to, from, next) => {
const { locale } = to.params
const publicPages = [ `/`, `/${locale}`, `/${locale}/`];
const authRequired = !publicPages.includes(to.path);
const loggedIn = localStorage.getItem('user');
const redirect = to.path;
loadLocaleMessagesAsync(locale).then(() => {
setDocumentLang(locale)
}).then(() => {
if (authRequired && loggedIn === null) {
if(to.meta.authRequired === false) {
debugger;
next();
}
else {
debugger;
next({ name: 'Login', query: { redirect: redirect } });
}
} else {
debugger;
next();
}
})
});
The root to this issue was a missing slash in my route.
pushRouteTo(`${$i18n.locale}/list/` + f.id)
should instead be:
pushRouteTo(`${/$i18n.locale}/list/` + f.id)
thats why it was creating a double locale.

Accessing to store in the router

I would like to check in my Vuex store whether a user has the 'admin' role before entering the /dashboard route. But I can't properly access data from store.getters.
I use Quasar (Vue.js) and Vuex + Typescript.
In the routes.ts file, on the beforeEnter() function, I can access getters from the store with a console.log(store.myStore.getters). Here I see userInfos inside:
I don't understand why I only get {} and not {...} (Note that if I click on it, I see its contents).
But if I call console.log(store.myStore.getters.userInfos), I don't see the data:
Here is index.ts (router):
import { route } from 'quasar/wrappers'
import VueRouter from 'vue-router'
import { Store } from 'vuex'
import { StateInterface } from '../store'
import routes from './routes'
export default route<Store<StateInterface>>(function ({ Vue }) {
Vue.use(VueRouter)
const Router = new VueRouter({
scrollBehavior: () => ({ x: 0, y: 0 }),
routes,
mode: process.env.VUE_ROUTER_MODE,
base: process.env.VUE_ROUTER_BASE
})
return Router
})
Here is routes.ts (router):
import { RouteConfig } from 'vue-router'
const routes: RouteConfig[] = [
{
path: '/',
component: () => import('layouts/Login.vue'),
children: [
{ path: '', component: () => import('pages/Index.vue') },
{ path: '/inscription', component: () => import('pages/SignUp.vue') },
{ path: '/connexion', component: () => import('pages/SignInPage.vue') }
]
},
{
path: '/main',
component: () => import('layouts/MainLayout.vue'),
children: [
{ path: '', component: () => import('pages/Index.vue') },
{ path: '/dashboard', component: () => import('pages/DashboardB2B.vue'),
beforeEnter: (to, from, next) => {
const store = require('../store')
console.log("before enter")
console.log(store.myStore.getters)
return next();
}, },
{
path: '/ajouter-un-referentiel',
component: () => import('pages/ReferentielMetier.vue')
},
{
path: '/init',
component: () => import('components/bot/BotSkeleton.vue')
}
]
},
{
path: '/bot',
component: () => import('layouts/Bot.vue'),
children: [
{
path: '/ajouter-un-referentiel',
component: () => import('pages/ReferentielMetier.vue')
},
{
path: '/init',
component: () => import('components/bot/BotSkeleton.vue')
}
]
},
// Always leave this as last one,
// but you can also remove it
{
path: '*',
component: () => import('pages/Error404.vue')
}
]
export default routes
And here is index.ts with the store (Vuex):
import Vue from 'vue'
import { store } from 'quasar/wrappers'
import Vuex from 'vuex'
Vue.use(Vuex)
import matching from './modules/matching'
import orga from './modules/organigrame'
import user from './modules/user'
export interface StateInterface {
example: unknown
}
let myStore: any
export default store(function({ Vue }) {
Vue.use(Vuex)
const Store = new Vuex.Store<StateInterface>({
modules: {
matching,
orga,
user
},
// enable strict mode (adds overhead!)
// for dev mode only
strict: !!process.env.DEBUGGING
})
myStore = Store
return Store
})
export {myStore}
EDIT: Looks like my console.log runs before the getters are loaded, because when I check out Vue developer tools, I see everything. How can I check the store if the store itself doesn't load before the beforeEnter function?
please try like this
store.myStore.getters["userInfos"]
I'm having exact issue, even if i use async/await :S
try this
router.beforeEach(async(to, from, next) => {
const userInfo = await store.getters.userInfos;
console.log(userInfo);
});

How to fix the default language in the url redirection

After upgrade from angular 7 to angular 8, my url is not redirected to /en, the en is my default language.
I use ng serve --configuration:en to generate the build for the English version, so I want when we have http://www.my-app.com --> redirect to http://www.my-app.com/en by default
import { NgModule } from "#angular/core";
import { Routes, RouterModule } from "#angular/router";
export const appRoutes: Routes = [
{
path: "m1",
loadChildren: () => import("./m1/m1.module").then(m => m.M1Module)
},
{
path: "m2",
loadChildren: () => import("./m2/m2.module").then(m => m.M2Module),
}, { path: "",
redirectTo: "m1/home",
pathMatch: "full"
},
];
#NgModule({
imports: [RouterModule.forRoot(appRoutes)],
exports: [RouterModule]
})
export class AppBrowserRoutingModule { }

How to test angular 7 component with ActiveRoute.snapshot.data and Router?

I have a TitleComponent that uses ActivedRoute.snapshot.data and Router to update data from app-routing.
This is my app-routing.module
export const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{
path: 'home',
component: MainHomeComponent,
data: {
title: 'Home',
}
},
];
#NgModule({
imports: [RouterModule.forRoot(routes, {
scrollPositionRestoration: 'enabled',
anchorScrolling: 'enabled',
scrollOffset: [0, 64] // [x, y]
} )],
exports: [RouterModule]
})
export class AppRoutingModule { }
and this is my TitleComponent.ts
export class TitleComponent implements OnInit {
titleTab: string;
subTitle: string;
isTablet$: Subscribable<boolean> = this.breakpointsService.isTablet$;
constructor(private router: Router, private route: ActivatedRoute, private breakpointsService: BreakpointsService) {
this.router.events.subscribe((data) => {
if (data instanceof NavigationEnd) {
console.log('title is= ',this.route.snapshot.data['title']);
// console.log(this.route.root.firstChild.snapshot.data.subTitle);
this.titleTab = this.route.snapshot.data['title'];
if (this.route.snapshot.data['subTitle']) {
this.subTitle = this.route.snapshot.data['subTitle'];
} else {
this.subTitle = '';
}
}
});
}
ngOnInit() {
}
}
Now i want to test my TitleComponent. For example, when i navigate to home, i want to have titleTab === Home or Actived.snapshot.data['title'] = Home
How do it? I tried many ways buti always have errors.
My spec.ts
fdescribe('TitleComponent', () => {
let component: TitleComponent;
let fixture: ComponentFixture<TitleComponent>;
let location: Location;
let router: Router;
// let route: ActivatedRoute;
const mockActivedRoute = jasmine.createSpyObj('ActivatedRoute', ['get']);
const testRouter = jasmine.createSpyObj('Router', ['get'])
const route = ({ data: of({ title: 'home' }) } as any) as ActivatedRoute;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
TitleComponent,
HomeComponent,
SearchComponent,
AppComponent,
MainHomeComponent,
],
providers: [
{
provide: ActivatedRoute,
useValue: {
snapshot: {
data: { title: 'home' }
}
},
}
],
imports: [RouterTestingModule.withRoutes(routes)],
schemas: [NO_ERRORS_SCHEMA],
}).compileComponents();
router = TestBed.get(Router);
location = TestBed.get(Location);
router.initialNavigation();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TitleComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
// It doesn't works with mock
it('test home data', fakeAsync(() => {
router.navigate(['/home']);
tick();
expect(route.snapshot.data.title).toBe('title');
}));
// It doesn't works with component
//it('test ', fakeAsync(() => {
//router.navigate(['/home']);
//tick();
//expect(component.titleTab).toBe('home');
// }));
Help me please.
Router navigation is asynchronous, so my guess is that you should wait for router event to complete. Here is what I would use:
it('should....', (done) => {
router.navigate(['/home'])
.then(() => {
fixture.detectChanges();
expect(....);
done();
})
.catch(err => fail(err));
});