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.
Related
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
I have a sidebar with some links. The sidebar is located at the /dashboard route. The links on the sidebar are direct children to /dashboard. I now want to render the children of /dashboard inside the main router-outlet. I have no idea on how to approach this.
The following are some code snippets to elaborate my question further
My routing structure
const routes: Routes = [
{
path: '',
component: LoginComponent,
},
{
path: 'dashboard',
component: DashboardComponent,
children: [
{
path: 'roles',
component: RolesComponent,
},
{
path: 'workgroups',
component: WorkgroupsComponent,
children: [
{
path: 'savewg',
component: WgDetailsComponent,
},
]
},
{
path: 'users',
component: UsersComponent,
},
],
},
];
App component
<!-- Main app component -->
<div class="app-view">
<router-outlet></router-outlet>
</div>
Login.html
<button mat-raised-button color="warn" class="login-field" (click)="login(email, password)"
<!-- rest of code ommited for brevity -->
Login.ts
public login(email: string, password: string) {
this.router.navigate(['dashboard'], { replaceUrl: true });
}
Workgroup Component html
<button mat-raised-button color="warn" [routerLink]="['savewg']">
<mat-icon>add</mat-icon>
New
</button>
<!-- Code ommited for brevity -->
<router-outlet></router-outlet>
<div class="workgroup-filters">
<mat-form-field appearance="outline">
<!-- rest of Code ommited for brevity -->
When I click on the new button in the workgroup component, I want it to navigate me to the savewg component view and replace the content in the workgroup component.
Any suggestions on how I can tackle this will be appreciated
Modules are only allow one main router-outlet per module. You will have to create a separate module for workgroups path and lazy load it. The workgroups module will have its own routing file and its own router outlet where you will load all your routes from your workgroup module. See below stackblitz for a working example.
app-routing.module.ts
const routes: Routes = [
...
{
path: 'workgroups',
loadChildren: () => import('./workgroups/workgroups.module').then(m => m.WorkgroupsModule)
},
...
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
workgroups-routing.module.ts
const routes: Routes = [
{
path: '',
component: WorkgroupsComponent,
children: [
{
path: 'savewg',
component: WgDetailsComponent
},
{
path: '**',
redirectTo: 'savewg',
pathMatch: 'full'
}
]
},
];
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class WorkgroupsRoutingModule { }
workgroups.module.ts
#NgModule({
imports: [
CommonModule,
WorkgroupsRoutingModule
],
declarations: [
WorkgroupsComponent,
WgDetailsComponent
]
})
export class WorkgroupsModule { }
Below are resources for lazy loading modules and a stackblitz example.
https://stackblitz.com/edit/angular-ivy-hbogtn
https://www.freakyjolly.com/angular-nested-routing-with-multiple-routeroutlet-using-loadchildren-having-own-router-modules-example-application/#.X3IQa3WYXmE
I found a better solution to this. It turns out that Angular will load the empty path route as the default route and there is no need to worry about router outlet anymore. I modified my app-routing.module.ts in the following way:
const routes: Routes = [
{
path: '',
component: LoginComponent,
},
{
path: 'dashboard',
component: DashboardComponent,
children: [
{
path: 'roles',
component: RolesComponent,
},
{
path: 'workgroups',
children: [
{
path: 'savewg',
component: WgDetailsComponent,
},
{
path: '',
component: WorkgroupsComponent,
},
],
},
{
path: 'users',
component: UsersComponent,
},
],
},
];
As you can see, I've added an empty path as a child in the
workgroups component This may not be perfect, more approaches and
solutions are welcome.
I'm Using nested modules in my project
.
└─ AppModule
├─ MallModule
├─ OtherModule
└─ ...
In the main route I only configured top-level routes:
app-routing.module.ts
const routes: Routes = [
{ path: '', redirectTo: '/', pathMatch: 'full' },
{ path: 'login', component: LoginComponent},
{ path: 'register', component: RegisterComponent },
{ path: '404', component: NotfoundComponent },
{ path: '**', redirectTo: '404' }, // Added
]
Separately, I configured routes separately in each sub-modules, like:
mall-routing.module.ts
const routes: Routes = [
{
path: '',
component: MallComponent,
children: [
{
path: '',
component: HomeComponent,
},
{
path: 'about',
component: AboutComponent,
},
...
}
]
The result is, because that no other routes are defined in the main routing configs, all requests other than login/register/404 will be redirected to 404.
Is there anyway I can use a correct 404 redirection but keep the current route file structure? I don't hope to gather all route configs together.
Thanks!
import the 'Other' modules in your app modules, this will allow the routes defined in those modules to be used.
The updated code should look something like this:
imports: [
MallModule,
OtherModule
RouterModule.forRoot([ // Add the configuration here, which is not a part of other module ])
]
in routing load your modules like below
// MallModule
{
path: "path",
canLoad: [AuthGuard],
loadChildren: "./modules/MallModule.module#MallModule",
},
I arrived in at this question after one module worked fine with the routing but another gave 404 for all its subpages.
The problem for me was the order in app.modules.ts. I had the submodules after the import of AppRoutingModule in the list of imports. Once I moved that last, than the routing of submodules and 404 page worked fine.
In app.module.ts.
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
SubModule,
AuthorizationModule,
CommonModule,
FontAwesomeModule,
ProfileModule, // this was previously defined after AppRoutinModule
AppRoutingModule // this must be last in list for routing with 404 to work
]
and in app-routing.modules.ts.
const routes: Routes = [
// https://angular.io/guide/router
{ path: 'help', component: HelpComponent },
{ path: 'terms', component: TermsComponent },
{ path: 'contact', component: ContactComponent},
{ path: '404', component: PageNotFoundComponent},
{ path: '**', component: PageNotFoundComponent}
];
I have an app component an its use router-outlet, and a main component (what is loaded at router-outlet)
Now I would like to place packages in that component where we can insert widgets.
App component:
<cover></cover>
<div fxFlexFill fxLayout="row" fxLayoutAlign="stretch">
<div fxFlex>
<router-outlet></router-outlet>
</div>
</div>
Main component:
<app-header></app-header>
<main-widget></main-widget> //place where I would like to load custom content
<left-widget></left-widget> //place where I would like to load custom content
<right-widget></right-widget> //place where I would like to load custom content
Main component is come from a coreModul(share module) - this is a framework we have no access to it on project level.
So I would like to put content into widgets, but have no idea how to do that.
The hard thing to me is how to pass a component into a router-outlet and show it in right place. Its possible to use ng-templates in this case?
EDIT:
App routing:
import { RouterModule, Routes } from '#angular/router';
import {
LoginComponent,
LoginGuard,
MainComponent,
} from './../_system';
import { USERS_ROUTES, UsersComponent } from './../users';
const APP_ROUTES: Routes = [
{ path: 'login', component: LoginComponent },
{ path: '', component:MainComponent, canActivate: [LoginGuard], children: [
{ path: 'users', children: USERS_ROUTES},
{ path: '', redirectTo: '/login', pathMatch: 'full'},
{ path: '**', redirectTo: '/questions'}
]}
];
export const AppRoutingModule = RouterModule.forRoot(APP_ROUTES);
In app.module.ts I load 2 lazy modules like this
const appRoutes: Routes = [
{ path: 'anonym', loadChildren: './anonym/anonym.module#AnonymModule' },
{ path: 'user', loadChildren: './user/user.module#UserModule', canActivate: [AuthGuard] }
];
In app.component.html I can write some base html. My question - is there any way to have base html for my UserModule?
I have tried to create user.component.ts and load it like in app.module.ts
import { UserComponent } from './user.component';
#NgModule({
declarations: [UserComponent],
bootstrap: [UserComponent]
});
but it's not show me user.component.html
In your user module define a base route with an empty path, and then define the sub-paths as children.
const ROUTES: Routes = [
{
path: '',
component: UsersComponent,
children: [
{
path: '',
pathMatch: 'full',
redirectTo: 'login'
}, {
path: 'login',
component: LoginComponent
}, {
path: 'logout',
component: LogoutComponent
}
]
}
];
The UsersComponent will now be used as a base component, and if you navigate to just /users it will redirect to the /users/login path.
Make sure your UsersComponent has a template with <router-outlet></router-outlet> so the child routes.
#NgModule({
imports: [
RouterModule.forChild(ROUTES)
],
exports: [
RouterModule
]
})
export class UsersModule {
}