Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested outlets in master layout outlet in angular 6

I am building an angular 6 application where I have two separate screens:

  1. Login (login, register, forgot password, profile)
    • those pages are not part of the layout
  2. Layout (dashboard, products, invoices)
    • those pages should share the same layout

My application is based on modules, so I have

  • app.module.ts
  • account.module.ts
  • dashboard.module.ts
  • products.module.ts
  • invoices.module.ts

Each module has empty component containing just an outlet, except the layout(containing header, body and footer).

I want to achieve those routes:

  • site.com/account/login
  • site.com | will navigate to the dashboard
    • site.com/dashboard
  • site.com/products
  • site.com/invoices

Visual explanation


I have no problem with adding the account.module, but I don't know how to configure the routes when adding the layout.module

Note: maybe my whole approach is wrong, but to me it is the only logical thing to do, since I have modules containing components, and I want to be prepared for possible lazy loading configuration.

Please advice me If I'm on the wrong path.

Code

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule } from '@angular/router';

import { AppComponent } from './app.component';

import { LayoutModule } from './layout/layout.module';
import { AccountModule } from './feature-components/membership/account.module';


@NgModule({
  declarations: [
    AppComponent,
    FetchDataComponent
  ],
  imports: [
    BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
    BrowserAnimationsModule,
    HttpClientModule,
    LayoutModule,
    AccountModule,
    BrowserAnimationsModule
    RouterModule.forRoot([
     { path: '', component: LayoutOutletComponent },
     { path: '**', component: PageNotFoundComponent }
    ]),
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.html

<router-outlet></router-outlet>

account.module.ts

import { NgModule } from '@angular/core';
import { SharedModule } from '@app-shared/shared.module';
import { LoginComponent } from './login/login.component';
import { ResetPasswordComponent } from './reset-password/reset-password.component';
import { ChangePasswordComponent } from './change-password/change-password.component';
import { RouterModule } from '@angular/router';
import { AccountOutletComponent } from './account-outlet/account-outlet.component';

@NgModule({
  imports: [
    SharedModule,
    RouterModule.forChild(
      [{
        path: 'account', component: AccountOutletComponent,
        children: [
          { path: 'login', component: LoginComponent },
          { path: 'reset-password', component: ResetPasswordComponent },
          { path: 'change-password', component: ChangePasswordComponent },
          { path: '', redirectTo: 'login', pathMatch: 'full' }
        ]
      }]
    )
  ],
  exports: [
    LoginComponent,
    ResetPasswordComponent,
    ChangePasswordComponent
  ],
  declarations: [
    AccountOutletComponent,
    LoginComponent,
    ResetPasswordComponent,
    ChangePasswordComponent
  ]
})
export class AccountModule { }

So, no problems here, the problems starts when I add that layout module which should constants the layout and inside of it, all nested modules.. and to be honest I don't even know how to start the routing configuration

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';

import { NavMenuComponent } from './nav-menu/nav-menu.component';
import { FooterComponent } from './footer/footer.component';
import { LayoutOutletComponent  } from './layout-outlet/layout-outlet.component';
import { InvoicesModule } from '../feature-components/invoices/invoices.module';


@NgModule({
  imports: [
    CommonModule,
    InvoicesModule,
    RouterModule
  )],
  exports: [
    NavMenuComponent,
    FooterComponent
  ],
  declarations: [
    NavMenuComponent,
    FooterComponent,
    LayoutOutletComponent
  ]
})
export class LayoutModule { }
like image 459
JustDontKnow Avatar asked Jun 20 '18 08:06

JustDontKnow


People also ask

Can we use multiple router-outlet in Angular 6?

Angular Router supports multiple outlets in the same application. A component has one associated primary route and can have auxiliary routes.

Can we use two router-outlet in Angular?

You can have multiple router-outlet in same template by configuring your router and providing name to your router-outlet, you can achieve this as follows. Advantage of below approach is thats you can avoid dirty looking URL with it. eg: /home(aux:login) etc.

How many router outlets can be used in an Angular application?

Remember all this route config only has one router-outlet , so the end result of the matching must be a single component and not multiple.

What is router-outlet router-outlet in Angular?

The Angular router-outlet Router-Outlet is an Angular directive from the router library that is used to insert the component matched by routes to be displayed on the screen.


2 Answers

I think you're on the right path. First you need to deal with eager routes and then you can switch to lazy modules.

What you need to do:

app.module.ts

@NgModule({
  imports: [
    BrowserModule,
    AccountModule,
    LayoutModule,
    RouterModule.forRoot([
      { path: '**', component: PageNotFoundComponent }
    ])
  ],
  declarations: [
    AppComponent,
    PageNotFoundComponent
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

layout.module.ts

@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild([
      {
        path: '',
        component: LayoutOutletComponent,
        children: [
          {
            path: 'dashboard',
            loadChildren: () => DashboardModule
          },
          {
            path: 'products',
            loadChildren: () => ProductsModule
          },
          { path: '', redirectTo: '/dashboard', pathMatch: 'full' }
        ]
      }
    ])
  ],
  declarations: [
    LayoutOutletComponent
  ]
})
export class LayoutModule { }

The main thing you need to know is that all angular routes are merged in one route configuration. To understand this I'd suggest you watching a great video by @deborahk

You can't just write

{ path: '', component: LayoutOutletComponent},

and import other modules(ProductsModule, DashboardModule) separately. The nested routes should be provided as child routes.

Now, when you have all routes configured you can easily switch to lazy loaded modules:

{
  path: '',
    component: LayoutOutletComponent,
    children: [
      {
        path: 'dashboard',
        loadChildren: 'pathToModule#DashboardModule'
      },
      {
        path: 'products',
        loadChildren: 'pathToModule#ProductsModule'
      },
      { path: '', redirectTo: '/dashboard', pathMatch: 'full' }
    ]
}

And also you can lazy load LayoutModule in AppModule

@NgModule({
  imports: [
   ...
    LayoutModule,
    RouterModule.forRoot([
      { path: '', loadChildren: './layout/layout.module#LayoutModule' },
      { path: '**', component: PageNotFoundComponent }
    ])
  ],
  ...
})
export class AppModule { }

I've created ng-nested-outlets app on github so try it out.

You can also try it online on Stackblitz Demo

See also

  • Angular switch from lazyLoading to 'normal' loading
like image 75
yurzui Avatar answered Oct 21 '22 02:10

yurzui


You shoud do the following things

  • in the app module create routes to load modules not the components
  • in layout module create routes to load Dashboard, Product and InvoicesModule
  • in layout component setup header router-outlet and footer and/or other layout elements
  • in modules Dashboard, Product and Invoices setup routes to load component

app.module

    RouterModule.forRoot([
      { path: '', loadChildren: '(PathToLayoutMoudle)#LayoutModule' },
      { path: '**', component: PageNotFoundComponent }
    ]),

layout.module

    RouterModule.forChild([
      { path: '', component: LayoutComponent},
          children: [
          { path: 'dashboard',loadChildren: 
              PathToDashboardMoudle)#DashboardMoudle'},
          { path: 'products',loadChildren: 
              PathToProductsMoudle)#ProductsMoudle'},
          { path: 'invoices',loadChildren: 
              PathToInvoicesMoudle)#InvoicesdMoudle'},
      ]
    ]),

account.module

  RouterModule.forChild(
  [{
    path: '', component: AccountOutletComponent,
    children: [
      { path: 'login', component: LoginComponent },
      { path: 'reset-password', component: ResetPasswordComponent },
      { path: 'change-password', component: ChangePasswordComponent },
      { path: '', redirectTo: 'login', pathMatch: 'full' }
    ]
  }]
like image 33
TomOw Avatar answered Oct 21 '22 02:10

TomOw