Angular - multiple optional route parameters - javascript

I can't believe I can't find this situation already covered here in SO:
(I found examples with additional parameter with and without for each single route, but it's unacceptable)
So I have
RouterModule.forRoot([
{
path: 'home',
component: HomeComponent
},
{
path: 'news',
component: NewsComponent
},
{
path: 'newsDetail/:id',
component: NewsDetailComponent
},
...
})
So the example URLs would be
http://somewhere.com/home
http://somewhere.com/news
http://somewhere.com/newsDetail/10
What if I want to add optional parameter to each of those URLs, so I can explicitly call another localization directly in URL (for permalinks):
http://somewhere.com/home/en
http://somewhere.com/news/en
http://somewhere.com/newsDetail/10/en
So it should work with and without "/en" at the end - and of course adding to each and every route (same route with optional /:language) is not the answer (imagine dozens of pages involved, many of them already with their own parameters)

If you want the language parameter to be the first, you can do the following. You will first have to declare an empty app or whatever root component and use this in the bootstrap instead of the AppComponent:
#Component({
selector: 'app-root',
template: `<router-outlet></router-outlet>`
})
export class RootComponent {}
Then create a module from your current routes, if you do not have that already. Call it AppRoutingModule.
export const AppRoutes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'news', component: NewsComponent },
{ path: 'newsDetail/:id', component: NewsDetailComponent }
];
#NgModule({
imports: [RouterModule.forFeature(AppRoutes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Then create a RootRoutingModule, which will do the magic:
export const RootRoutes: Routes = [
{ path: '', loadChildren: () => import('./app.module').then((m) => m.AppModule) },
{ path: 'en', loadChildren: () => import('./app.module').then((m) => m.AppModule) }
];
#NgModule({
imports: [RouterModule.forRoot(AppRoutes)],
exports: [RouterModule],
})
export class RootRoutingModule {}
The issue with this, is that you'll have to hardcode all the languages you might support, as I don't think a :language parameter will work
So basically, create a root module which will do the language routing and the bootstrapping

Related

Lazy loading for child routes is not redirecting to child

Hi for me parent routing is working fine, but for chid routes it is not redirecting and not getting errors also
in app.routing
const routes: Routes = [
{
path: 'card',
loadChildren: './cards/cards.module#CardsModule',
canActivate: [AuthGuard]
},
{
path: 'card/:id',
loadChildren: './cards/cards.module#CardsModule',
canActivate: [AuthGuard]
}]
in card-routing:
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { Cardscomponent } from './cards.component';
import { AuthGuard } from '../_services/auth.guard';
import { CardDetailsComponent } from './card-details/card-details.component';
const routes: Routes = [
{ path: '', component: Cardscomponent,
children :[
{ path: ':id', component: CardDetailsComponent}
]
}
]
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class CardsRoutingModule { }
localhost:4200/card is working
but localhost:4200/card/1234 is not working and not getting any error also, any help...
Check whether you have router-outlet in child component.
When the url updated but view not rendered, it usually comes down to missing router-outlet.
Learned that from developing a entire project with angular elements.
If you enable tracing, you can see all the router events are triggered successfully.

Angular CanLoad guard triggers only once upon first lazy load?

I'm encountering a strange behaviour (or maybe a wanted one). I have an Angular application where all the modules are lazy loaded.
On one module I have a guard that checks if the decoded user from the JWT is a system admin. If so, the user shall proceed to the section, otherwise it will be redirected in dashboard.
The strange thing is that this thing works only upon the first module load. Then, if I try to logout and access with a user that is not as system admin, the CanLoad guard does not trigger.
I've also tried to implement in the same guard the (CanActivate and CanActivateChild) interfaces, and put the guard on the app-routing.module.ts and on the feature-routing.module.ts modules, respectively on the CanLoad, CanActivate, and CanActivateChild properties of the modules.
The CanActivate CanActivateChild methods never gets called. Never.
While the CanLoad placed on the app-routing.module.ts is called just once.
Here's the is-sys-adm.guard.ts file:
export class SFWIsSysAdmGuard implements CanLoad, CanActivate, CanActivateChild {
public constructor(
private readonly accessSvc: SFWAuthService,
private readonly toastSvc: NbToastrService,
private readonly translateSvc: TranslateService,
private readonly navigationSvc: NavigationService,
) { }
public canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
console.log('Can activate child hit');
return this.canLoad(undefined, undefined);
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
console.log('Can activate hit');
return this.canLoad(undefined, undefined);
}
public canLoad(route: Route, segments: UrlSegment[]): boolean {
console.log('Can load hit');
const decodedUser = this.accessSvc.decodedUser;
if (!!decodedUser && !!decodedUser.isSystemAdmin) {
return true;
}
this.navigationSvc.goToDashboard();
this.toastSvc.warning(
this.translateSvc.instant('framework.guards.adm_only.access_denied'),
this.translateSvc.instant('common.access_to_section_denied_ttl'),
);
return false;
}
}
Here's the app-routing.module.ts file:
const routes: Routes = [
{
path: '',
redirectTo: `/${AppRoutes.access}`,
pathMatch: 'full'
},
{
path: AppRoutes.dashboard,
loadChildren: () => import('./dashboard/dashboard.module').then(mod => mod.DashboardModule)
},
{
path: AppRoutes.pins,
loadChildren: () => import('./pins/pins.module').then(mod => mod.PinsModule)
},
{
path: AppRoutes.pinTypes,
loadChildren: () => import('./pin-types/pin-types.module').then(mod => mod.PinTypesModule)
},
{
path: AppRoutes.organizationPickup,
loadChildren: () => import('./organization-picker/organization-picker.module').then(mod => mod.OrganizationPickerModule)
},
{
path: AppRoutes.access,
loadChildren: () => import('./access/access.module').then(mod => mod.AccessModule)
},
{
path: AppRoutes.tourism,
loadChildren: () => import('./tourism/tourism.module').then(mod => mod.TourismModule)
},
{
path: AppRoutes.security,
canLoad: [SFWIsSysAdmGuard],
loadChildren: () => import('./security/security.module').then(mod => mod.SecurityModule)
},
{
path: AppRoutes.notFound,
loadChildren: () => import('./not-found/not-found.module').then(mod => mod.NotFoundModule)
},
{
path: '**',
redirectTo: '/404'
}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Last but not least: feature-routing.module.ts file:
const routes: Routes = [
{
path: '',
canActivate: [SFWIsSysAdmGuard],
component: SecurityComponent,
children: [
{
path: 'tokens-generator',
canActivateChild: [SFWIsSysAdmGuard],
component: TokensGeneratorComponent
},
]
}
];
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class SecurityRoutingModule { }
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class SecurityRoutingModule { }
Before you ask, I've also tried to put the guard separately in a CanActivate, CanActivateChild, CanLoad to try to prevent any conflict (which shouldn't exist if I've understand the docs.)
Am I missing something? Is is a wanted behaviour or should I open a bug on the official repo?
Thank to anyone willing to spend time on this <3
The CanLoad determines whether the lazy module can be loaded from the server. After it has been loaded, this will not be checked again (unless you press F5). I guess you need to declare it twice, once in CanLoad (if you don't want the code to be loaded at all), and CanActivate, if you want to restrict access.
{
path: AppRoutes.security,
canLoad: [SFWIsSysAdmGuard],
canActivate: [SFWIsSysAdmGuard],
loadChildren: () => import('./security/security.module').then(mod => mod.SecurityModule)
},

Angular router not loading the proper component

I am beginner with angular and I have the followings routes.
app.routing.module.ts
import { NgModule } from '#angular/core';
import { RouterModule } from '#angular/router';
import { FrameComponent } from './ui/frame/frame.component';
import { NotFoundComponent } from './common/not-found/not-found.component';
const routes = [
{
path: 'login',
loadChildren: 'src/app/login/login.module#LoginModule'
},
{
path: 'dashboard',
component: FrameComponent,
loadChildren: 'src/app/dashboard/dashboard.module#DashboardModule'
},
{
path: "**",
component: NotFoundComponent,
}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
dashboard.routing.module.ts
import { NgModule } from '#angular/core';
import { RouterModule } from '#angular/router';
import { OverviewComponent } from '../overview/overview.component';
const routes = [
{
path: '',
children:[
{
path: 'overview',
component: OverviewComponent,
//outlet: 'dashboard-inside'
}
]
},
];
#NgModule({
imports: [
RouterModule.forChild(routes)
],
exports: [RouterModule]
})
export class DashboardRoutingModule { }
When navigating to /dashboard it loads the FrameComponent from the AppRoutingModule.
But when navigating to /dashboard/overview it loads NotFoundComponent instead of OverviewComponent from second router.
I am still a beginner with Angular. What am I doing wrong?
I think you didn't define your routes correctly
{
path: 'dashboard',
component: FrameComponent,
loadChildren: 'src/app/dashboard/dashboard.module#DashboardModule'
}
This piece of code doesn't load lazily - you are not loading the childern over here you are just loading the component FrameComponent so angular does it for you
If your FrameComponent is part of AppModule you can just remove the loadChildren from the path and the angular will do the same routing for you
If it is not the part of AppModule then try something like this
app-routing.module.ts
{
path: 'dashboard',
loadChildren: 'src/app/dashboard/dashboard.module#DashboardModule'
}
Just load another module from the path and load the component you want from that module
dashboard-routing.module.ts
{
path: '',
component: FrameComponent,
children:[
{
path: 'overview',
component: OverviewComponent,
//outlet: 'dashboard-inside'
}
]
}
Make sure you have declared the FrameComponemt inside the DashboardModule and that will make you to load the route you want
Now if the path is /dashboard angular will load the dashboard module and check for the path '' next to the /dashboard so it will load the FrameComponent then when you try to access path /dashboard/overview routing will load the child route and OverviewComponet will be loaded
Hope everything will work good - please feel free to reach me if you have any doubts - Happy coding :)
You can remove component: FrameComponent from the dashboard route and move it into the dashboard routing module.
{
path: 'dashboard',
loadChildren: 'src/app/dashboard/dashboard.module#DashboardModule'
},
{
path: '',
component: FrameComponent,
children:[
{
path: 'overview',
component: OverviewComponent,
}
]
},
And I guess you should import your modules in core one.
Your definition in dashboard.routing.module.ts is wrong.
Try this instead:
import { NgModule } from '#angular/core';
import { RouterModule } from '#angular/router';
import { OverviewComponent } from '../overview/overview.component';
const routes = [
{
path: 'overview', // <-- should be in root.
component: OverviewComponent,
},
];
#NgModule({
imports: [
RouterModule.forChild(routes)
],
exports: [RouterModule]
})
export class DashboardRoutingModule { }

Error when creating a navigate function through button click using router Angular 6

I am new to angular 6 and it's navigation/routing, I am sorry if this sounds obvious.
I am trying to have a button with a function to navigate to the login page, but I keep getting this error:
" Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'login'
Error: Cannot match any routes. URL Segment: 'login'
at ApplyRedirects.push../node_modules/#angular/router/fesm5/router.js.ApplyRedirects.noMatchError "
my html is :
<button ion-button (click) = "openLogin()" >Efetuar login</button>
my router is :
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { LoginComponent } from './login/login.component';
const routes: Routes = [
{
path: 'login',
outlet: 'login',
component: LoginComponent
},
{ path: '', loadChildren: './tabs/tabs.module#TabsPageModule' }
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
I've been researching on the matter and as long as the path corresponds to the path defined in the router there shouldn't be any issue but I keep getting this error.
Named router-outlet is quite different than normal router-outlet.
This is how named router-outlet works
1.Add outlet to your route object as you are already doing now
{
path: 'login',
outlet: 'login',
component: LoginComponent
},
2.Add a name attribute to router-outlet tag
<router-outlet name="login"></router-outlet>
3.Using router.navigate
this.router.navigate([{ outlets: { login: [ 'login' ] }}]);
4.Using routerLink
[routerLink]="[{ outlets: { login: ['login'] } }]"
I would recommend you to read this post aswell.
Try to implement this in your imports this way:
RouterModule.forRoot(routes, {useHash: true});
instead of:
RouterModule.forRoot(routes)

Split routes into separate modules in angular 6

I'm working on an Angular 6 application. Currently I'm struggling with routing. I'm interested, whether mine structure, what I have imagined can work or not. So it looks like this:
App module - contains main routing with some parent route, where layout is defined. Like this:
const routes: Routes = [
{
path: 'login',
component: LoginComponent
},
{
path: '',
component: LayoutComponent,
canActivate: [AuthGuard],
canActivateChild: [AuthGuard],
children: [
// {
// path: 'brands',
// loadChildren: 'app/modules/brands/brands.module#BrandsModule',
// pathMatch: 'prefix'
// }
]
}
];
#NgModule({
imports: [RouterModule.forRoot(routes), BrandsModule, ItemsModule],
exports: [RouterModule],
providers: [BosRouteLoader]
})
export class RoutingModule {}
One of mine feature module defines its own routing in a module like this:
const routes: Routes = [
{
path: 'brands',
children: [
{ path: '', component: BrandListComponent },
{ path: ':id', component: BrandDetailComponent },
{ path: '**', redirectTo: '' }
]
}];
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class BrandsRoutingModule {}
I would like to achieve that every feature module will define its own routing and those routes are registered as a child routes of the App module.
With lazy loading I can manage it but then I have to define one more route always in mine App module, however I just want to define it in feature module.
If I do it without lazy loading, then mine parent route in App component is never hit. So if I go to http://localhost/brands it will load the appropriate BrandLisComponent but without LayoutComponent.
Is ther a way to define routes in feature module and register them as a child of main main routing module?
The concept is that you define a module routing in your higher-level module and then define its children in your desired module.
So in your case, you need to tell the angular that, hey when someone goes to brands route, use BrandsRoutingModule routings.
So in your app module, you're going to have:
{
path: 'brands',
loadChildren: 'app/modules/brands/brands.module#BrandsModule',
canActivate: [AuthGuard],
canActivateChild: [AuthGuard],
}
This tells that you need to load the routing of that module if the user goes to /brand.
Then in your BrandsRoutingModule, you need to define routes to be:
{
path: '',
component: LayoutComponent,
children: [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: BrandListComponent },
{ path: ':id', component: BrandDetailComponent },
{ path: '**', redirectTo: '' }
]
}
So whenever we route to /brands, we shall see the LayoutComponent as a main route relative to that and then the BrandListComponent and others will come as his children. But to show his children, you also need to put this line of code in your layout.component.html:
<router-outlet></router-outlet>
this tells angular that, hey if he's going to for example /brands/2, you need to load the BrandDetailComponent inside the LayoutComponent, literally as his child.
Hope it helps.
Imans77's answer works for lazy loaded modules (although the string from of LoadChildren is now deprecated). However, with eager loaded modules, if you want to tidy up the main routes module and split the file accross different modules, you could try the following approach:
app-routing.module.ts
const MODULE_ROUTES = [...module1Routes, module2Routes];
const routes: Routes = [
{ path: 'path1', component: Path1Component },
{ path: 'path2', component: Path2Component },
...MODULE_ROUTES,
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Instead of declaring all children of every component/module in the main routing file, you could create a *.route.ts file for each feature module, in which you could just declare and export routes in a normal way. For example:
export const module1Routes: Routes = [
{
path: 'brands',
children: [
{ path: '', component: BrandListComponent },
{ path: ':id', component: BrandDetailComponent },
{ path: '**', redirectTo: '' }
]
}];
By importing it in the main routing file, they would immediately be available for Angular.

Categories

Resources