Using Angular Modules & Optimizing Apps
understanding the App.Module
import tells Typescript what files to use
WebPack goes through the imports to bundle the app
Angular modules define how our app looks to Angular
in NgModule declaration imports property is an array of modules
when a module is imported everything the module exports is imported
in app-routing module the NgModule imports the RouterModule and applies the Route array then the module exports the configured RouterModule
    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    ...

    const appRoutes: Routes = [...];
    @NgModule({
        imports: [RouterModule.forRoot(appRoutes)],
        exports: [RouterModule]
    })
    export class AppRoutingModule { }
        
importing modules avoids the need to explicitly import the needed types individually
modules are bundles of functionality

Top

Index

understanding feature modules
custom module built for an app
in course project all recipe-related components could be in a features module

Top

Index

registering routes in a feature module
in recipes folder add recipes-routing.module
cut recipe-related paths from app-routing.module and paste them into the new module
import the RouterModule and use the forChild method to load the recipeRoutes array and export the RouterModule
    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    ...
    const recipesRoutes: Routes = [
        {
            path: 'recipes', component: RecipesComponent, children: [
                { path: '', component: RecipeStartComponent },
                { path: 'new', component: RecipeEditComponent, canActivate: [AuthGuard] },
                // paths without dynamic IDs must be first
                { path: ':id', component: RecipeDetailComponent },
                { path: ':id/edit', component: RecipeEditComponent, canActivate: [AuthGuard] },
            ]
        },
    ];
    @NgModule({
        imports: [RouterModule.forChild(recipesRoutes)],
        exports: [RouterModule]
    })
    export class RecipesRoutingModule { }
        
in the recipes.module import the new module and add it to the NgModule's imports property
    ...
    import { RecipesRoutingModule } from './recipes-routing.module';

    @NgModule({
        declarations: [
            ...
        ],
        imports: [
            ...,
            RecipesRoutingModule
        ],
    })
    export class RecipesModule { }
        

Top

Index

understanding shared modules
the DropdownDirective is needed by both the app component and the feature module
put DropdownDirective into a shared module
add shared.module.ts in shared folder
typically only one shared module in an app

Top

Index

creating a shared module
the module exports both the DropdownDirective and the CommonModule
    import { NgModule } from '@angular/core';
    import { CommonModule } from '@angular/common';

    import { DropdownDirective } from './dropdown.directive';

    @NgModule({
        declarations: [
            DropdownDirective
        ],
        imports: [
            CommonModule
        ],
        exports: [
            CommonModule,
            DropdownDirective
        ]
    })
    export class SharedModule { }
        
in app.module remove the DropdownDirective import and declaration and import the shared module
    ...
    // import { DropdownDirective } from './shared/dropdown.directive';
    import { SharedModule } from './shared/shared.module';
    ...
    @NgModule({
      declarations: [
        ...
        // DropdownDirective,
        SignInComponent,
        SignUpComponent
      ],
      imports: [
        ...
        SharedModule
      ],
      ...
    })
    export class AppModule { }
        
in recipes.module import the SharedModule
remove references to CommonModule as it is exported by the shared module
    ...
    import { SharedModule } from '../shared/shared.module';

    @NgModule({
        declarations: [
            ...
        ],
        imports: [
            ReactiveFormsModule,
            RecipesRoutingModule,
            SharedModule
        ],
    })
    export class RecipesModule { }
        

Top

Index

loading components via selectors vs routing
for selector have to declare it in the module where it is to be used
can import and export type from different module

Top

Index

understanding lazy loading
lazy loading - feature modules can be loaded as needed
add new component using cli
    ng g c home --spec false
        
in AppRoutingModule change the empty path to the HomeComponent
add a recipe path as shown below
    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';

    import { ShoppingListComponent } from './shopping-list/shopping-list.component';
    import { HomeComponent } from './home/home.component';

    const appRoutes: Routes = [
        { path: '', component: HomeComponent },
        // loadChildren: < path to feature model >#< class name >
        { path: 'recipe', loadChildren: './recipes/recipes.module#RecipesModule' },
        { path: 'shopping-list', component: ShoppingListComponent },
    ];
    @NgModule({
        imports: [RouterModule.forRoot(appRoutes)],
        exports: [RouterModule]
    })
    export class AppRoutingModule { }
        
recipe module is now lazily loaded

Top

Index

protecting lazy loading routes with canLoad
route protection on lazily loaded routes can be done using canActivate
better to check that BEFORE loading the code. use the canLoad guard to the route which points to the lazily loaded module
    { path: 'recipes', loadChildren: './recipes/recipes.module#RecipesModule', canLoad: [AuthGuard] } 
        
AuthGuard needs to implement the CanLoad interface
    import { ActivatedRouteSnapshot, CanActivate, CanLoad, Route, RouterStateSnapshot } from '@angular/router';
    import { Injectable } from '@angular/core';
    import { AuthService } from './auth.service';
    import { Observable } from 'rxjs/Observable';

    @Injectable()
    export class AuthGuard implements CanActivate, CanLoad {

        constructor(private authService: AuthService) { }
        canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
            return this.authService.isAuthenticated();
        }
        canLoad(route: Route): Observable<boolean> | Promise<boolean> | boolean {
            return this.authService.isAuthenticated();
        }
    }
        
add the canLoad property to the JSON object in the app-routing module
    ...
    import { AuthGuard} from './auth/auth-guard.service';

    const appRoutes: Routes = [
        { path: '', component: HomeComponent },
        { path: 'recipe', loadChildren: './recipes/recipes.module#RecipesModule', canLoad: [AuthGuard] },
        { path: 'shopping-list', component: ShoppingListComponent },
    ];
    ...
    export class AppRoutingModule { }
        

Top

Index

how modules and services work together
consider an app with an eagerly-loaded feature module and a lazily-loaded feature module
both the app module and the eagerly-loaded feature module have declared a log service in their providers array
the Root Injector creates one instance of the service at application launch and both modules share the instance
when the lazily-loaded module is loaded and the log service is injected, the module will use the same single instance
if the lazily-loaded module has the log service listed in its provider array things are different
when the module is lazily-loaded Angular will use a Child Injector to create a new instance of the service
this behavior is only for modules
components use a hierarchital injector

in the first case add a shared module which includes the log service in its providers array
when the lazily-load module is loaded it gets a different instance of the service because the child injector is used

don't provide services in shared modules especially if the services are to be used by lazily-loaded modules

Top

Index

creating a basic core module
core modules only imported by the root module
place for single instance, global services
also be used to export any third party module that is required in the AppModule

Top

Index

using Ahead-of-Time compilation
JIT Compilation
  1. development
  2. production
  3. app downloaded in browser
  4. Angular parses & compiles templates to JavaScript
Ahead-of-time Compilation
  1. development
  2. Angular parses & compiles templates to JavaScript
  3. production
  4. app downloaded in browser
advantages of AoT compilation

Top

Index

how to use AoT compilation with the CLI
the command
    ng build
            
builds the app for JIT compiling
the command
    ng build --prod           
     
tries to optimize and minimize the code
the command
    ng build --prod --aot          
     
uses ahead of time compilation

Top

Index

preloading lazy loaded routes
preload lazy loaded modules rather than wait for demand
in app-routing.module add second arg to forRoot method
arg is a JSON object with property called preloadingStrategy
when preloadingStrategy is set to PreloadAllModules lazily loaded modules are loaded after the app is loaded
     ...
    const appRoutes: Routes = [ ... ];
    @NgModule({
        imports: [RouterModule.forRoot(appRoutes, { preloadingStrategy: PreloadAllModules })],
        exports: [RouterModule]
    })
    export class AppRoutingModule { }
        

Top
Index

n4jvp.com