I am having a problem where my parent component (LoggedInComponent) is getting reloaded every time one of the child components changes (child route change).
I have searched high and low for an answer but can't seem to find anything suitable to my situation.
Here is my app-routing.module.ts
const routes: Routes = [
{ path: 'login', component: LoginComponent },
{
path: '', component: LoggedInComponent, canActivateChild: [AuthGuard], children: [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: DashboardComponent },
{ path: 'groups', component: GroupsComponent, data: { role: [PermissionEnum.Groups_View] } },
{ path: 'groups/edit/:id', component: GroupDetailComponent, data: { role: [PermissionEnum.Groups_Edit] } },
{ path: 'groups/create', component: GroupDetailComponent, data: { role: [PermissionEnum.Groups_Create] } },
{ path: 'users', component: UsersComponent, data: { role: [PermissionEnum.Users_View] } },
{ path: 'users/edit/:id', component: UserDetailComponent, data: { role: [PermissionEnum.Users_Edit] } },
{ path: 'users/create', component: UserDetailComponent, data: { role: [PermissionEnum.Users_Create] } },
{ path: 'profile', component: ProfileComponent },
{ path: 'profile/:tabindex', component: ProfileComponent },
{ path: 'settings', component: SettingComponent, data: { role: [PermissionEnum.Global_Settings_View] } },
{ path: 'external-login/:result', component: ExternalLoginProvidersComponent },
{ path: 'permissions/:id/:type', component: PermissionsComponent, data: { role: [PermissionEnum.Users_AssignPermissions] } },
{ path: 'permission-denied', component: PermissionDeniedComponent },
{ path: 'reference-data/:type', component: ReferenceDataComponent, data: { role: [PermissionEnum.Sms_Template_View] } },
{ path: 'reference-data/:type/edit/:id', component: ReferenceDataDetailsComponent, data: { role: [PermissionEnum.Sms_Template_Edit] } },
{ path: 'reference-data/:type/create', component: ReferenceDataDetailsComponent, data: { role: [PermissionEnum.Sms_Template_Create] } },
{ path: 'tenants', component: TenantsComponent, data: { role: [PermissionEnum.Tenant_View] } },
{ path: 'tenants/edit/:id', component: TenantDetailComponent, data: { role: [PermissionEnum.Tenant_Edit] } },
{ path: 'tenants/create', component: TenantDetailComponent, data: { role: [PermissionEnum.Tenant_Create] } },
{ path: 'sms-campaigns', component: SmsCampaignsComponent, data: { role: [PermissionEnum.SmsCampaign_View] } },
{ path: 'sms-campaigns/create', component: CreateSmsCampaignComponent, data: { role: [PermissionEnum.SmsCampaign_Create] } },
{ path: 'sms-campaigns/details/:id', component: SmsCampaignDetailsComponent, data: { role: [PermissionEnum.SmsCampaign_View] } },
{ path: 'document-library', component: LibraryDocumentsComponent },
{ path: 'report-management', component: ReportManagementComponent },
{ path: 'report-management/create', component: CreateReportComponent },
{ path: 'report-management/:id', component: IdpComponent },
{ path: 'report-management/edit/:id', component: ReportDetailsComponent },
{ path: 'report/:reportName', component: ReportComponent }
]
}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
I have the main router-outlet in my app.component.html which after loggin in takes you to the LoggedInComponenet which has the header, footer, left menu and another router-outlet for the children.
This is my LoggedIn.componenent.html
<app-header></app-header>
<div class="m-grid__item m-grid__item--fluid m-grid m-grid--ver-desktop m-grid--desktop m-body">
<app-left-menu></app-left-menu>
<div *ngIf="loading">
<app-loading-indicator></app-loading-indicator>
</div>
<div class="center-display" *ngIf="childrenLoadingAllowed">
<router-outlet class="m-grid__item m-grid__item--fluid m-wrapper" [ngClass]="{ hidden: loading }"></router-outlet>
</div>
</div>
<app-footer></app-footer>
I then have my LoggedIn.component.ts
import { Component, OnInit } from '#angular/core';
import { BaseComponent } from '../shared/base.component';
#Component({
selector: 'app-logged-in',
templateUrl: './logged-in.component.html',
styleUrls: ['./logged-in.component.css']
})
export class LoggedInComponent extends BaseComponent implements OnInit {
public loading = true;
public childrenLoadingAllowed = false;
constructor() {
super();
}
ngOnInit() {
this.layoutService.setLoadingEvent
.subscribe((res: boolean) => {
if (this.loading !== res)
this.loading = res;
});
}
}
And then finally here is the left-menu which keeps reloading when i load a child
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { BaseComponent } from '../../shared/base.component';
import { PermissionEnum, LookupClient, LookupType, LookUpDto } from '../../../services/web-api-generated';
#Component({
selector: 'app-left-menu',
templateUrl: './left-menu.component.html',
styleUrls: ['./left-menu.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class LeftMenuComponent extends BaseComponent implements OnInit {
public hasReports = false;
public reports: Array<LookUpDto> = new Array<LookUpDto>();
constructor(private lookupClient: LookupClient) {
super();
this.loadReportMenuItems();
}
ngOnInit() {
this.layoutService.rebuildReportMenu
.subscribe(res => {
this.loadReportMenuItems();
});
}
private loadReportMenuItems(): void {
this.lookupClient.getLookUpValues(LookupType.MunicipalReports)
.subscribe((res: Array<LookUpDto>) => {
this.reports = res;
this.reports.forEach(element => {
element.value = element.value.replace(/\s+/g, '-').toLocaleLowerCase();
});
this.hasReports = res.length > 0;
});
}
}
I fixed the problem by moving the api call to a service with a variable there and only loading the data if its not already set or if the force variable is passed through.
I believe this is a bug as mentioned here: https://github.com/angular/angular/issues/18374
yes, canActivateChild reloads whole parent component while changing between child routes
Related
I have a problem. I made routing what according how many addresses have user it will display them in manner 'Addresses(addrescount)'. global variable for getting address count is '$auth.user.address_count'
Task was if user have addresses show their count in sidebar if user do not have addresses show nothing .I did that but is any chance to have another solution to this problem
Is any chance to make it different then it now is?
how it looks like in programm
see line before {{route.display}}
<template>
<div class="col-lg-3">
<div class="profile-sidebar">
<ul class="list-unstyled">
<li v-for="route in nodes" :key="route.name">
<router-link :to="route.fullPath">
<template v-if="route.display === route.meta.title && $auth.user.address_count>0">
{{ route.display }}({{$auth.user.address_count}})
</template>
<template v-else>
{{ route.display }}
</template>
</router-link>
</li>
</ul>
</div>
</div>
</template>
<script>
import { routes } from '#/routes/routes';
export default {
data() {
return {
nodes: [],
}
},
async created() {
if (!this.$auth.user)
await this.$auth.updateUserInfo();
this.loadProfileNodes();
},
methods: {
loadProfileNodes() {
let node = routes.filter(route => route.path === '/profile').pop();
let hasLocalAccount = this.$auth.hasLocalAccount;
this.nodes = [];
node.children.forEach((route) => {
route['fullPath'] = node.path + '/' + route.path;
if ((!hasLocalAccount && route.path !== 'change-password') || (hasLocalAccount && route.path !== 'set-password')) {
this.nodes.push(route);
}
});
}
},
}
</script>
my routes.js see /profile children address-list route.
const routeOptions = [
{ path: '/', name: 'default', view: 'home', display: 'Home', meta: { showInMenu: true } },
{ path: '/401-forbidden', name: 'forbidden', view: 'errors/401-forbidden', display: '401 Forbidden' },
{ path: '/404-not-found', name: 'not-found', view: 'errors/404-not-found', display: '404 Page Not Found' },
{ path: '/login', name: 'login', view: 'auth/login' },
{ path: '/register', name: 'register', view: 'auth/register' },
{ path: '/auth/forgot-password', view: 'auth/forgot-password' },
{ path: '/auth/reset-password', view: 'auth/reset-password', props: (route) => ({ code: route.query.code }) },
{ path: '/auth/confirm-email', view: 'auth/confirm-email', props: (route) => ({ userId: route.query.userId, token: route.query.token }) },
{ path: '/admin/user-list', view: 'admin/users/user-list', display: 'Users', meta: { showInMenu: true, auth: { roles: 'Admin' } } },
{ path: '/admin/company-list', view: 'admin/companies/company-list', display: 'Companies', meta: { showInMenu: true, auth: { roles: 'Admin' } } },
{
path: '/profile',
view: 'profile/profile',
display: 'Edit profile',
meta: { auth: true },
children: [
{
path: '',
display: 'My Profile',
view: 'profile/edit-profile',
meta: { auth: true }
},
{
path: 'manage-logins',
display: 'External Logins',
view: 'profile/manage-logins',
meta: { auth: true }
},
{
path: 'address-list',
display: 'Addresses',
view: 'profile/addresses/address-list',
meta: { auth: true, title: 'Addresses' }
},
{
path: 'change-password',
display: 'Change Password',
view: 'profile/change-password',
meta: { auth: true }
},
{
path: 'set-password',
display: 'Set Password',
view: 'profile/set-password',
meta: { auth: true }
}
]
},
{ path: '*', redirect: { name: 'not-found' } }
];
function addDynamicImport(route) {
if (!route.view)
return route;
if (route.children && route.children.length) {
route.children = route.children.map(child => {
return addDynamicImport(child);
});
}
return {
...route,
component: () => import(/* webpackChunkName: "[request]" */ `#/components/views/${route.view}`)
}
}
const routes = routeOptions.map(route => {
return addDynamicImport(route);
})
export { routes }
router.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import { routes } from './routes';
Vue.use(VueRouter);
let router = new VueRouter({
mode: 'history',
routes
});
export default router;
Answer is simple. You can add flag into '/profile/addres-list' route meta tag and check it if it is true/ True means we found the needed page
{
path: 'address-list',
display: 'Addresses',
view: 'profile/addresses/address-list',
meta: { auth: true, title: 'Addresses' }
},
into this (difference in the last line)
{
path: 'address-list',
display: 'Addresses',
view: 'profile/addresses/address-list',
meta: { auth: true, isAddressList: true }
}
and change
<template v-if="route.display === route.meta.title &&$auth.user.address_count>0">
to this
<template v-if="route.meta.isAddressList && $auth.user.address_count>0">
I want to send data from home component via book.vue router link, but I am getting an error. Where am I doing wrong?
Home.vue
export default {
data() {
return {
data: {
attributes: {
name: 'Jonh',
age: '25',
},
},
}
},
}
router/index.js:
const routes = [
{ path: '/Library/:id', name: 'Book', component: Book, props: true },
]
navigation:
:to="{ name: 'Book', params: { id: book.id}, props: { data: data.attributes} }"
Book.vue
export default {
props: {
id: {
type: Array,
required: true
}
}
}
I need to recover the password of the users via email, the problem is that when I pass a link via email for example http://my-domain/recovery-password/token, it always redirects me to my login component (http://my-domain/login), what I need is go directly to my recovery-password component. I'm a little new in vue and I don't know what I need to change, this is my code in the router:
const routes = [
{
path: '/',
name: 'home',
component: Home,
meta: {
user_type: 1
}
},
{
path: '/check-balance',
name: 'check-balance',
component: CheckBalanceComponent,
meta: {
user_type: 1
}
},
{
path: '/check-payment',
name: 'check-payment',
component: CheckPaymentsComponent,
meta: {
user_type: 1
}
},
{
path: '/payment-disabled',
name: 'payment-disabled',
component: DisabledMakePaymentComponent,
meta: {
user_type: 1
}
},
{
path: '/handle-payment',
name: 'handle-payment',
component: HandlePaymentsComponent,
meta: {
user_type: 1
}
},
{
path: '/handle-report',
name: 'handle-report',
component: HandleReportsComponent,
meta: {
user_type: 1
}
},
{
path: '/user-profile',
name: 'user-profile',
component: UserProfileComponent,
meta: {
user_type: 1
}
},
{
path: '/login',
name: 'login',
component: LoginComponent,
meta: {
free: 1
}
},
{
path: '/recover-link',
name: 'recover-link',
component: RecoverLinkComponent,
meta: {
free: 1
}
},
{
path: '/recover-password',
name: 'recover-password',
component: RecoverPasswordComponent,
meta: {
free: 1
}
},
{
path: '/help',
name: 'help',
component: HelpComponent,
meta: {
user_type: 1
}
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
router.beforeEach((to, from, next) => {
if(to.matched.some(record => record.meta.free)){
next()
} else if(store.state.user && store.state.user.user_type == 1){
next()
// } else if(to.matched.some(record => record.meta.fuera)){
// next({
// name: 'recover-password'
// })
} else {
next({
name: 'login'
})
}
})
export default router
I would appreciate your help because I have been standing for a long time without finding a solution
I think Vue looks through all the routes to find which one has free prop as part of its meta object.
And it always find Login route first before it ever gets to recover-password route - its reading top to bottom in the Routes Array.
So, try putting recover-password route before login route like so:
{
path: '/recover-password',
name: 'recover-password',
component: RecoverPasswordComponent,
meta: {
free: 1
}
},
{
path: '/login',
name: 'login',
component: LoginComponent,
meta: {
free: 1
}
}
However, you may start encountering that recover component renders in places where you may need login component.
So, the best way to solve this whole issue is by giving a different free value to both routes and then check for that value in your beforeEach hook.
#nishkaush
This is my vue router code with the changes you suggested to me:
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: Home,
meta: {
user_type: 1
}
},
{
path: '/check-balance',
name: 'check-balance',
component: CheckBalanceComponent,
meta: {
user_type: 1
}
},
{
path: '/check-payment',
name: 'check-payment',
component: CheckPaymentsComponent,
meta: {
user_type: 1
}
},
{
path: '/payment-disabled',
name: 'payment-disabled',
component: DisabledMakePaymentComponent,
meta: {
user_type: 1
}
},
{
path: '/handle-payment',
name: 'handle-payment',
component: HandlePaymentsComponent,
meta: {
user_type: 1
}
},
{
path: '/handle-report',
name: 'handle-report',
component: HandleReportsComponent,
meta: {
user_type: 1
}
},
{
path: '/user-profile',
name: 'user-profile',
component: UserProfileComponent,
meta: {
user_type: 1
}
},
{
path: '/recover-password',
name: 'recover-password',
component: RecoverPasswordComponent,
meta: {
other: 1
}
},
{
path: '/recover-link',
name: 'recover-link',
component: RecoverLinkComponent,
meta: {
free: 1
}
},
{
path: '/login',
name: 'login',
component: LoginComponent,
params: {
recovery_email: 'email'
},
meta: {
free: 1
}
},
{
path: '/help',
name: 'help',
component: HelpComponent,
meta: {
user_type: 1
}
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
router.beforeEach((to, from, next) => {
if(to.matched.some(record => record.meta.free)){
next()
}
else if(store.state.user && store.state.user.user_type == 1){
next()
}
/*This is where I have to call my recover-password component,
from an external link without redirecting to the login*/
else if(to.matched.some(record => record.meta.other)){
next()
}
else {
next({
name: 'login'
})
}
})
export default router
In Ionic 4, I have an application that features a Master-Detail pattern on the Home Component. My tabs work for the highest-level (component) parents in the router tree.
However, my tabs disappear when I navigate to the Detail Page.
[An image of the detail page goes here.]
app-routing.module.ts
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
const routes: Routes = [
// { path: '', redirectTo: '/checklists', pathMatch: 'full' },
// { path: '', redirectTo: '/tabs', pathMatch: 'full' },
// { path: 'tabs', loadChildren: './tabs/tabs.module#TabsPageModule' },
{ path: '', loadChildren: './tabs/tabs.module#TabsPageModule' },
{ path: 'intro', loadChildren: './intro/intro.module#IntroPageModule' },
// Checklists are added to the tab bar here (or not)
{ path: 'checklists', loadChildren: './home/home.module#HomePageModule' }, // Tab Bar is Visible
// { path: 'checklists/:id', loadChildren: './checklist/checklist.module#ChecklistPageModule' }, // Tab Bar is Not Visible
{ path: 'checklists/:id',
children: [
{ path: '',
loadChildren: './checklist/checklist.module#ChecklistPageModule'
},
]
}, // Tab Bar is Not Visible
// Make checklists/:id into a NESTED route path? Nested, Auxiliary, and Child Routes
{ path: 'about', loadChildren: './about/about.module#AboutPageModule' },
{ path: 'contact', loadChildren: './contact/contact.module#ContactPageModule' }
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
tabs.router.module.ts
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { TabsPage } from './tabs.page';
import { HomePage } from '../home/home.page';
import { AboutPage } from '../about/about.page';
import { ContactPage } from '../contact/contact.page';
import { ChecklistPage } from '../checklist/checklist.page';
let tabsPath = 'tabs';
let tabsComponent = TabsPage;
let tabsEmptyPath = '';
let tabsRouteBasic = '/tabs/(home:home)';
// How do I turn the details of the About Tab into a variable?
// How does the router path/state get transported around in navigation parameters?
let aboutPath = 'about';
let aboutOutletName = 'about';
const routes: Routes = [
{
path: tabsPath,
component: tabsComponent,
children: [
{
path: tabsEmptyPath,
redirectTo: tabsRouteBasic,
pathMatch: 'full',
},
{
path: 'home',
outlet: 'home',
component: HomePage
},
{
path: 'checklist/:id', // This is temporary
outlet: 'home',
component: tabsComponent
},
{
path: 'checklist', // This is temporary
outlet: 'checklist',
component: ChecklistPage
},
// I would like to turn this path into a variable AND dynamically create its related component
{
path: aboutPath,
outlet: aboutOutletName,
component: AboutPage
},
// I would like to turn this path into a variable AND dynamically create its related component
{
path: 'contact',
outlet: 'contact',
component: ContactPage
}
]
},
{
path: '',
redirectTo: '/tabs/(home:home)',
pathMatch: 'full'
}
];
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class TabsPageRoutingModule {}
I'm using the route.navigate(['page']); conditionally and it does not work.
e.g
constructor(private route: Router){}
if(data.user != null) {
this.route.navigate(['page']);
} else {
alert('Please Login');
}
The URL changes but the page don't render.
This is my App Route Module
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
// App Components
import { HeaderComponent } from './header/header.component';
import { HomeComponent } from './home/home.component';
import { ResultsListComponent } from './results-list/results-list.component';
import { SigninComponent } from './signin/signin.component';
import { AffordabilityComponent } from
'./affordability/affordability.component';
import { RegisterComponent } from './register/register.component';
import { ShowroomComponent } from './showroom/showroom.component';
import { AdminComponent } from './admin/admin.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { OffersComponent } from './offers/offers.component';
import { AdminHeaderComponent } from './admin-header/admin-
header.component';
import { FooterComponent } from './footer/footer.component';
import { DealerProfileComponent } from './dealer-profile/dealer-
profile.component';
const routes: Routes = [
{ path: '', component: HomeComponent },
// { path: '**', redirectTo: '', pathMatch: 'full' },
{ path: 'search_results', component: ResultsListComponent },
{ path: 'signin', component: SigninComponent },
{ path: 'results', component: AffordabilityComponent },
{ path: 'register', component: RegisterComponent },
{ path: 'showroom', component: ShowroomComponent },
{ path: 'admin', component: AdminComponent },
{ path: 'dashboard', component: DashboardComponent },
{ path: 'offers', component: OffersComponent },
{ path: 'profile', component: DealerProfileComponent }
];
#NgModule({
imports: [RouterModule.forRoot(routes, { enableTracing: true })],
exports: [RouterModule]
})
export class AppRoutingModule { }
export const routingComponents = [
HeaderComponent,
HomeComponent,
ResultsListComponent,
SigninComponent,
AffordabilityComponent,
RegisterComponent,
ShowroomComponent,
AdminComponent,
DashboardComponent,
OffersComponent,
AdminHeaderComponent,
FooterComponent,
DealerProfileComponent
];