Search code examples
angularlazy-loadingangular9angular-lazyloading

How to create/use lazyloading login component in Angular 9?


I have a login component in my Angular project, and it's worked fine. But now I changed my software's structure from single module to lazy loading modules.

I created a PublicModule with LoginComponent, ForgotPasswordComponent and RegistrationComponent.

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, Injector, LOCALE_ID, NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AuthService } from './services/auth/auth.service';
import { AuthInterceptorService } from './services/auth/auth-interceptor.service';
export let InjectorInstance: Injector;

@NgModule({
  declarations: [
    AppComponent,
  ],
  entryComponents: [],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
  ],
  providers: [
    { provide: LOCALE_ID, useValue: 'hu-HU' },
    { provide: ErrorHandler, useClass: AppErrorHandler },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptorService,
      multi: true
    },
    AuthService,
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
  constructor(private injector: Injector) {
    InjectorInstance = this.injector;
  }
}

app-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './services/auth/auth.guard';
import { DashboardComponent } from './components/dashboard/dashboard.component';

const routes: Routes = [
  { path: 'public', loadChildren: () => import('./modules/public/public.module').then(m => m.PublicModule) },

  { canActivate: [AuthGuard], path: '', component: DashboardComponent },
  { canActivate: [AuthGuard], path: '**', component: DashboardComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

app.component.html

<div *ngIf="authService.user.value">
  <!-- Logged in -->
  <router-outlet></router-outlet>
</div>
<div *ngIf="!authService.user.value">
  <!-- Not logged in -->
  <app-login></app-login>
</div>

app.component.ts

import { Component } from '@angular/core';
import { AuthService } from './services/auth/auth.service';
import { InjectorInstance } from './app.module';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'My Awesome Project';
  authService: AuthService = InjectorInstance.get<AuthService>(AuthService);

  constructor() {
    this.authService.autoLogin();
  }
}

I don't get any errors at build time, nor rendering time. Everythings looking fine, but the LoginComponent isn't show.

If I log the authService.user.value it's null, so the <app-login></app-login> shold be visible, but it isn't.

Any idea where should I look for the cause of the "error"?


Solution

  • I don't think there is a problem with the code itself but more with the concept.

    You correctly created the lazy loaded module under the public path, which means that when you go to /public it will load the module chunk (check the network tab to confirm it). This module should have routing as well, here's how I would set it up:

    const routes: Routes = [
      { path: 'forgot-password', component: ForgotPasswordComponent },
      { path: 'register', component: RegistrationComponent },
      { path: '', component: LoginComponent },
    ];
    
    @NgModule({
      imports: [RouterModule.forChild(routes)],
      exports: [RouterModule],
    })
    export class PublicRoutingModule {}
    

    Of course, this routing module needs to be imported in your public module to work.

    I noticed that you have an AuthGuard, which should check if the user is logged in or not. If not, should trigger a redirect to /public. You can find a snippet of AuthGuard doing a redirect here https://twitter.com/michelepatrassi/status/1177193819848216576/photo/1.

    So, your template becomes quite simple

    <router-outlet></router-outlet>
    

    If the user is logged in, the router-outlet will contain the dashboard. If not, the user will be redirected to public and will contain the login component (if you follow the routing example I mentioned).