I was trying to navigate from a parent component (Core) using an url dynamically emitted from a child component (Menubar).
The problem is that the navigation is cancelled without any reason.
To find out if the problem came from the parent component, I implemented a button in the parent component navigating to a static url, and it worked!
Core template:
<app-menubar (onNavigation)="onMenubarNavigation($event)"></app-menubar>
<button type="button" (click)="navigate()">Navigate</button>
<router-outlet><router-outlet>
Core component:
onMenubarNavigation(urlSegments: string[]): void {
this.router.navigate(urlSegments);
}
navigate(): void {
let segments: string[];
segments = ['index', 'messages'];
this.router.navigate(segments);
}
Menubar template:
<a (click)="changeRoute(element.module.route)">
// element.module route is the string: "/messages"
Menubar component:
#Output() onNavigation = new EventEmitter<string[]>();
changeRoute(url: string): void {
let urlSegments: string[];
urlSegments = url.split('/');
urlSegments[0] = 'index';
this.onNavigation.emit(urlSegments);
}
App routing module:
const routes: Routes = [
{ path: 'login', loadChildren: '.\/auth\/auth.module#AuthModule' },
{ path: 'logout', loadChildren: '.\/auth\/auth.module#AuthModule' },
{ path: '', loadChildren: './theme/core/app.core.module#AppCoreModule', pathMatch: 'full' },
];
#NgModule({
imports: [RouterModule.forRoot(routes, { enableTracing: true })],
exports: [RouterModule]
})
export class AppRoutingModule { }
Core routing module:
const coreRoutes: Routes = [
{
path: '',
component: AppCoreComponent,
canActivate: [AuthGuard],
children: [
{
path: 'home',
loadChildren: '..\/home\/app.home.module#AppHomeModule'
},
{
path: 'index',
loadChildren: '..\/index\/app.index.module#AppIndexModule'
},
{
path: '',
redirectTo: 'index',
pathMatch: 'full'
}
]
}
];
#NgModule({
imports: [
RouterModule.forChild(coreRoutes)
],
exports: [
RouterModule
]
})
export class AppCoreRoutingModule {}
The angular routing debug shows that both urls are the same during navigation, using the one emitted by the child component or the static one, but when I navigate using the one emitted, the navigation is cancelled and I have no idea why...
Has someone ever encoutered this kind of trouble with the angular router?
Related
I configured my app in two levels for nested routing as shown in the snapshot image.
the content of app modules is:
app.module.ts
#NgModule({
declarations: [
AppComponent,
DashboardComponent,
TeacherMembershipComponent,
StudentMembershipComponent,
RulesComponent,
ContactInfoComponent,
Page404Component,
UserProfileComponent,
NotificationsComponent,
GeneralComponent,
ReportComponent
],
// ...
and app-routing.module.ts
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent
},
{
path: 'userProfile',
component: UserProfileComponent
},
{
path: 'general',
component: GeneralComponent
},
{
path: 'report',
component: ReportComponent
},
{
path: 'notifications',
component: NotificationsComponent
},
{
path: "department/:dep", loadChildren: () => import(`./branches/branches.module`).then(m => m.BranchesModule)},
{
path: 'aboutUs/rules',
component: RulesComponent
},
{
path: 'aboutUs/contactInfo',
component: ContactInfoComponent
},
{
path: 'membership/teacher',
component: TeacherMembershipComponent
},
{
path: 'membership/student',
component: StudentMembershipComponent
},
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
{ path: '**', component: Page404Component },
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
the second level is branches that its modules are:
branches.module.ts:
#NgModule({
declarations: [
BranchesComponent,
CoursesListComponent,
TeachersListComponent,
TeacherResumeComponent,
TeacherSelectionFormComponent,
Page404BranchesComponent
],
imports: [
CommonModule,
BranchesRoutingModule,
MaterialDesignModule
]
})
export class BranchesModule { }
and branches-routing.module.ts:
const routes: Routes = [
{ path: '', component: BranchesComponent, children: [
{
path: "coursesList/:branchCourses",
component: CoursesListComponent
},
{
path: "teacherSelection",
component: TeacherSelectionFormComponent
},
{
path: "teacherResume",
component: TeacherResumeComponent
},
{
path: 'coursesList', component: CoursesListComponent
},
{
path: '', redirectTo: 'coursesList', pathMatch: 'full'
},
{
path: '**', component: Page404BranchesComponent
},
]}
];
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
till here, when one of links (departments) in sidenav of app.component.html is clicked, the branches.component is loaded in place of route-outlet of app.component and subscribes the parameters passed from app.component, and lists branches respected to each link.
baranch.component.ts:
export class BranchesComponent implements OnInit {
filteredBranches$!: Observable<Branch[]>;
filteredBranches!: Branch[];
constructor(private route: ActivatedRoute, private branchesService: BranchesService) {
}
activeTab: string=''
ngOnInit(): void {
this.route.paramMap.pipe(
switchMap((params: Params) => {return this.branchesService.getDepBranches(params['get']('dep')) })
).subscribe((branches) => {this.filteredBranches = branches; this.activeTab=this.filteredBranches[0].name});
}
}
and branch.component.html:
<nav mat-tab-nav-bar>
<a mat-tab-link *ngFor="let br of filteredBranches"
[routerLink]="['coursesList', br.code]"
routerLinkActive="activeLink"
(click)="activeTab = br.name"
[active]="activeTab == br.name"> {{ br.name }} </a>
</nav>
<router-outlet></router-outlet>
but, although links of branches are listed in template of branches.component, no courses respect to each branches are shown by coursesList.component which placed in router-outlet of branches.component, because it does not received any route parameter from branch.component, until one of branch link is clicked.
I expect that upon branches links are shown at the same time branches.component is loaded, bellow of them, the courses of the first branch are listed by coursesList.component placed in router-outlet of branches.component.
Whereas no one of branch links is clicked and no parameter passed to coursesList.component, nothing is shown. Therefore, is there a solution to active the first branch link as defualt and pass its courses parameters to coursesList component upon branches.component is loaded? Best regards.
I have angular project and I need when user type the URL like www.mysite.com/hisName , Take this name and add it to all routing pages in my project and the final result will be like www.mysite.com/hisName/home
this is my routing code
import { NgModule } from "#angular/core";
import { Routes, RouterModule } from "#angular/router";
import { AppComponent } from "./app.component";
const routes: Routes = [
{ path: "", redirectTo: "home", pathMatch: "full" },
{ path: "home", component: HomeComponent },
path: "supliers",
loadChildren: () =>
import("./supliers-container/supliers-container.module").then(
(mod) => mod.SupliersContainerModule
),
}
#NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true })],
exports: [RouterModule],
})
export class AppRoutingModule {}
I think you are asking for something like a wildcard option? It does have to be defined as the last element.
const routes: Routes = [
{ path: "", redirectTo: "home", pathMatch: "full" },
{ path: "home", component: HomeComponent },
{ path: "supliers",
loadChildren: () =>
import("./supliers-container/supliers-container.module").then(
(mod) => mod.SupliersContainerModule
),
},
{ path "**", redirectTo: "home", pathMatch: "full" } // here
]
Note: if you add the wildcard, I don't think you need the first path option.
I am facing some issue while setting up the routing in the angular 8.
I am doing it like:
'company/:id/activity'
'company/:id/contacts'
did not get any params in activatedRoute:
this.activateRoute.params
.subscribe(param => console.log(param) )
Is there any luck to fix that?
Main Routing file:
{
path: 'company/:id',
children: [
{
path: '',
loadChildren: () => import('#app/features/company-view/company-view.module').then(m => m.CompanyViewModule)
}
]
}
Lazy loaded routing file:
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { CompanyViewComponent } from '#app/features/company-view/company-view.component';
import { PendingChangesGuard } from '#app/shared/guards';
import { CompanyActivityComponent } from './company-activity/company-activity.component';
const routes: Routes = [
{
path: '',
component: CompanyViewComponent,
children: [
{
path: '',
redirectTo: 'view'
},
{
path: 'activity',
component: CompanyActivityComponent,
data: {
title: "Company Activity"
}
}
]
}
];
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class CompanyViewRoutingModule { }
if I use routing below then it works fine:
const routes: Routes = [
{
path: '',
component: CompanyViewComponent,
children: [
{
path: ':id/activity',
component: CompanyActivityComponent,
data: {
title: "Company Activity"
}
},
{
path: '**',
redirectTo: 'activity'
}
]
}
];
then how do I set the default routing to :id/activity ?
So. You want to pass variables for your component through the routing.
In your routes.ts is oke like this path: 'company/:id/:activity'.
Then you need to give it to your component.
I'm using this: id = this.route.snapshot.params['id'] or activity = this.route.snapshot.params['activity'].
From here you can pass through for another component or whatever you want.
Ex:
<app-testcomponent
[id]="id"
[activity]="activity">
</app-testcomponent>
I'm not seeing a route for this: "view".
{
path: '',
redirectTo: 'view'
}
I changed it to "activity" and changed it to your catch all route for that section of the tree. I also refactored your routes a little bit, please look at both:
Main Route
{
path: 'company',
children: [
{
path: '',
loadChildren: () => import('#app/features/company-view/company-view.module').then(m => m.CompanyViewModule)
}
]
}
Child Routes
const routes: Routes = [
{
path: ':id',
component: CompanyViewComponent,
children: [
{
path: 'activity',
component: CompanyActivityComponent,
data: {
title: "Company Activity"
}
},
{
path: '**',
redirectTo: 'activity'
}
]
}
];
I have my application set up where many of my components are protected. However, the user is still able to access the main page "/". I was wondering if it would be possible to redirect the user to /login if they are not authenticated without making ALL of my components children of whatever component I have on "/". I'll include a modified version of what I have below:
const routes: Routes = [
{
path: "login",
component: LoginComponent
},
{
path: "test",
component: TestComponent
},
{
path: "protected",
canActivate: [AuthGuardService],
component: ProtectedComponent
},
{
path: "alsoprotected/:id",
component: AlsoProtectedComponent,
canActivate: [AuthGuardService],
children: [
{ path: "child1", component: ChildOneComponent},
{ path: "child2", component: ChildTwoComponent},
{ path: "child3", component: ChildThreeComponent },
{ path: "child4", component: ChildFourComponent },
{ path: "child5", component: ChildFiveComponent },
{ path: "child6", component: ChildSixComponent },
{ path: "child7", component: ChildSevenComponent }
]
},
{
path: "protectedsettings",
canActivate: [AuthGuardService],
component: SettingsComponent
}
];
Is there some way to add my GuardService to my app-root component?
You can create a componentless route in the same level as login and move everything other than login inside that route. Then add the guard to that route.
const routes: Routes = [
{
path: "login",
component: LoginComponent
},
{
path: "",
canActivate: [AuthGuardService],
children: [
{
path: "test",
component: TestComponent
},
{
path: "protected",
canActivate: [AuthGuardService],
component: ProtectedComponent
},
{
path: "alsoprotected/:id",
component: AlsoProtectedComponent,
canActivate: [AuthGuardService],
children: [
{path: "child1", component: ChildOneComponent},
{path: "child2", component: ChildTwoComponent},
{path: "child3", component: ChildThreeComponent},
{path: "child4", component: ChildFourComponent},
{path: "child5", component: ChildFiveComponent},
{path: "child6", component: ChildSixComponent},
{path: "child7", component: ChildSevenComponent}
]
},
{
path: "protectedsettings",
canActivate: [AuthGuardService],
component: SettingsComponent
}
]
}
];
check this link for more info
Inside your app root component in the constructor you could use the router to catch the instance NavigationStart from the router.
export class AppComponent {
constructor(private router: Router) {
router.events.subscribe( (event: Event) => {
if (event instanceof NavigationStart) {
//Check either LocalStorage or cookies for value
if(!localStorage.GetItem('hasLoaded')){
this.router.navigate['./login']
}
}
});
}
}
Another option is a CanDeactiveRoute in your routing file
import { Injectable } from '#angular/core';
import { CanDeactivate } from '#angular/router';
import { Observable } from 'rxjs';
export interface CanComponentDeactivate {
canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}
#Injectable()
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
canDeactivate(component: CanComponentDeactivate) {
return component.canDeactivate ? component.canDeactivate() : true;
}
}
Using this check alongside localStorage is a good start, you could use something like server side sessions and a HTTP Interceptor alongside this also.
Be advised the user can disable this and it's not meant to obscure or hide sensitive data. Do that on a server using secure transmission mechanisms, you've been warned.
I have a problem with Angular routing. I have main app routing module and sub module with its own routing module and router-outlet but routes defined in this submodule are shown using root router outlet and not the child one.
My folder structure:
My code listings
app-routing.module.ts
const routes: Routes = [
{ path: '', component: HomeComponent, pathMatch: 'full' }
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
app.component.html
<router-outlet></router-outlet>
home-routing.module.ts
const routes: Routes = [
{ path: '', component: LandingPageComponent},
{ path: 'register', component: RegisterComponent },
{ path: 'login', component: LoginComponent }
];
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class HomeRoutingModule { }
home.component.html
...
<div class="inner cover">
<router-outlet></router-outlet>
</div>
...
That's what I get when I use empty path - it opens home component properly.
But when i enter /register i get plain html from login.component.html without template in home.component.html file
EDIT
I added name to child outlet
<router-outlet name="home"></router-outlet>
Changed route names to:
const routes: Routes = [
{ path: '', component: LandingPageComponent, outlet: 'home'},
{ path: 'register', component: RegisterComponent, outlet: 'home' },
{ path: 'login', component: LoginComponent, outlet: 'home' }
];
Now I got that error:
EDIT 2
I try to access those routes in 2 ways:
A link(which may be incorrect):
<a routerLink="/login">Log In</a></li>
Or typing manually:
localhost:4200/login
In Angular 2, router outlets can be named:
<router-outlet>
<router-outlet name="children"></router-outlet>
</router-outlet>
App:
const routes: Routes = [
{ path: '', component: HomeComponent, pathMatch: 'full' }
];
Home:
const routes: Routes = [
{ path: '', component: LandingPageComponent, outlet: 'children'},
{ path: 'register', component: RegisterComponent, outlet: 'children' },
{ path: 'login', component: LoginComponent, outlet: 'children' }
];
You can even define child routes:
const routes: Routes = [
{ path: '',
component: HomeComponent,
pathMatch: 'full', children: [
{ path: '', component: LandingPageComponent, outlet: 'children'},
{ path: 'register', component: RegisterComponent, outlet: 'children' },
{ path: 'login', component: LoginComponent, outlet: 'children' }
]
}
];
http://onehungrymind.com/named-router-outlets-in-angular-2/
If you want those 3 components to be rendered inside of the HomeComponent in a named outlet, then you need to define the following routes:
const routes: Routes = [
{ path: '',
component: HomeComponent,
pathMatch: 'full'
},
{ path: 'landing', component: LandingPageComponent, outlet: 'children'},
{ path: 'register', component: RegisterComponent, outlet: 'children' },
{ path: 'login', component: LoginComponent, outlet: 'children' }
];
And inside of app.component.html add the named router outlet
....//html template
<router-outlet name="children"></router-outlet>
....//html template
EDIT 1:
To navigate to the named outlets you need to use the following routed links:
//inside of home.component.html
<a [routerLink]="[{ outlets: { children: ['login'] } }]">Take me to login!</a>
The generated link will look like:
root/(children:login)
More info in the following link to the docs
EDIT 2:
I changed the original routes and the component template where the named outlet is added. Why?
it is not possible, as far as I know, to have a named outlet with an empty path (''). The empty path tells angular that the named outlet is empty (no component is currently rendered in it).
I believe your problem has to do with declarations. I can not know for certain since you didn't show the code in your app.module.ts and home.module.ts file and have not tested this fully myself.
A component needs to be declared inside the module connected to the template with the desired routing outlet. In your case the login component would need to be added to "declarations" in your home.module.ts file and removed from "declarations" in the app.module.ts file.
Angular does not seem allow you to reuse the same component in multiple routing-outlets unless they are in the same template, since it would cause an error stating "x component declared in multiple modules".