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.
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 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 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?
I just started learning angular 2 and cloned this repo:
https://github.com/AngularClass/angular-starter. Just adding an extra menu option called 'list'. I have created the component and added it to the routes:
export const ROUTES: Routes = [
{ path: '', component: HomeComponent },
{ path: 'home', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'detail', loadChildren: './+detail#DetailModule'},
{ path: 'barrel', loadChildren: './+barrel#BarrelModule'},
{ path: '**', component: NoContentComponent },
{ path: 'list', component: ListComponent}
];
This is the list component:
import {
Component,
OnInit
} from '#angular/core';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'list',
styles: [`
`],
template: `
<h1>list</h1>
<div>
For hot module reloading run
<pre>npm run start:hmr</pre>
</div>
<div>
<h3>
patrick#AngularClass.com
</h3>
</div>
<pre>this.localState = {{ localState | json }}</pre>
`
})
export class ListComponent implements OnInit {
public localState: any;
constructor(
public route: ActivatedRoute
) {}
public ngOnInit() {
this.route
.data
.subscribe((data: any) => {
/**
* Your resolved data from route.
*/
this.localState = data.yourData;
});
console.log('hello `List` component');
/**
* static data that is bundled
* var mockData = require('assets/mock-data/mock-data.json');
* console.log('mockData', mockData);
* if you're working with mock data you can also use http.get('assets/mock-data/mock-data.json')
*/
this.asyncDataWithWebpack();
}
private asyncDataWithWebpack() {
/**
* you can also async load mock data with 'es6-promise-loader'
* you would do this if you don't want the mock-data bundled
* remember that 'es6-promise-loader' is a promise
*/
setTimeout(() => {
System.import('../../assets/mock-data/mock-data.json')
.then((json) => {
console.log('async mockData', json);
this.localState = json;
});
});
}
}
For some reason I get this errormessage when I hit http://localhost:3000/#/list:
404 page missing
How can I solve this?
Move the list route to above the ** route like below:
export const ROUTES: Routes = [
{ path: '', component: HomeComponent },
{ path: 'home', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'detail', loadChildren: './+detail#DetailModule'},
{ path: 'barrel', loadChildren: './+barrel#BarrelModule'},
{ path: 'list', component: ListComponent},
{ path: '**', component: NoContentComponent }
];
The router uses a first match wins strategy for matching routes. So, if the wildcard route ** is declared above 'list', the wild card route prevails.
Read the official router documentation here.
Just rearrange the order of routes in your app.routes.ts file and it should work.
export const ROUTES: Routes = [
{ path: '', component: HomeComponent },
{ path: 'home', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'list', component: ListComponent},
{ path: 'detail', loadChildren: './+detail#DetailModule'},
{ path: 'barrel', loadChildren: './+barrel#BarrelModule'},
{ path: '**', component: NoContentComponent }
];
I have an Angular2 project built with this Angular 2 Webpack Starter but I cannot get the fallback route to work correctly. In my app.routes.ts I have:
import { Routes } from '#angular/router';
import { HomeComponent } from './home';
import { DataResolver } from './app.resolver';
export const ROUTES: Routes = [
{
path: '',
component: HomeComponent
},
{
path: 'getstarted', loadChildren: './getstarted#GetStartedModule'
},
...
{
path: 'notfound', loadChildren: './notfound#NotFoundModule'
},
{
path: '**', loadChildren: './notfound#NotFoundModule'
},
];
The not found path above works correctly but the fallback route (**) does not work correctly. Instead of showing the NotFoundModule it does not load a module at all and I get no errors. However, when I do this it redirects correctly:
...
{
path: 'notfound', loadChildren: './notfound#NotFoundModule'
},
{
path: '**', redirectTo:'/notfound', pathMatch: 'full'
},
];
I do not want to redirect though because I do not want to change the url to /notfound by redirecting. How can I make my top version work or what else can I do to make this work?
So, I just tried it and it seems that you cannot use lazy routes to set your fallback page. This should work :
export const ROUTES: Routes = [
{
path: '',
component: HomeComponent
},
{
path: 'getstarted', loadChildren: './getstarted#GetStartedModule'
},
...
{
path: 'notfound', loadChildren: './notfound#NotFoundModule'
},
{
path: '**', component : NotFoundComponent
},
];