3 - Generating components and Nx lib

1. Generate our first Nx lib

  • Run the below command to see all the lib options

nx g lib --help

The output for the current Nx version (9.3.0) is:

>nx g lib --help
nx generate @nrwl/angular:library [name] [options,...]
Options:
  --name                  Library name
  --directory             A directory where the lib is placed
  --publishable           Generate a buildable library.
  --prefix                The prefix to apply to generated selectors.
  --skipFormat            Skip formatting files
  --simpleModuleName      Keep the module name simple (when using --directory)
  --skipPackageJson       Do not add dependencies to package.json.
  --skipTsConfig          Do not update tsconfig.json for development experience.
  --style                 The file extension to be used for style files. (default: css)
  --routing               Add router configuration. See lazy for more information.
  --lazy                  Add RouterModule.forChild when set to true, and a simple array of routes when set to false.
  --parentModule          Update the router configuration of the parent module using loadChildren or children, depending on what `lazy` is set to.
  --tags                  Add tags to the library (used for linting)
  --unitTestRunner        Test runner to use for unit tests (default: jest)
  --dryRun                Runs through and reports activity without writing to disk.
  --help                  Show available options for project target.
  • Add a new lib called auth. We will not lazy load this lib as auth will always be used by our app. Choose SASS as your styelsheet language.

nx generate @nrwl/angular:lib auth --routing

The output should look like this:

? Which stylesheet format would you like to use? SASS(.scss)  [ http://sass-lang.com   ]
CREATE libs/auth/README.md (132 bytes)
CREATE libs/auth/tsconfig.lib.json (408 bytes)
CREATE libs/auth/tslint.json (247 bytes)
CREATE libs/auth/src/index.ts (35 bytes)
CREATE libs/auth/src/lib/auth.module.ts (269 bytes)
CREATE libs/auth/src/lib/auth.module.spec.ts (339 bytes)
CREATE libs/auth/tsconfig.json (123 bytes)
CREATE libs/auth/jest.config.js (347 bytes)
CREATE libs/auth/tsconfig.spec.json (233 bytes)
CREATE libs/auth/src/test-setup.ts (30 bytes)
UPDATE workspace.json (10310 bytes)
UPDATE nx.json (788 bytes)
UPDATE tsconfig.json (565 bytes)
  • Inspect the index.ts file which is for exporting public api surface for the lib.

  • Inspect angular.json libs array allow us to use --project flag with libs to get cli scaffolding and code generation.

2. Add container and presentational components

Lets look at what this pattern is and what are the benefits in slides https://docs.google.com/presentation/d/1xf8aPIvQjgjUVGH_1sRkikvh5H73x2xvX7PnN4AjYt4/edit#slide=id.g3bc936a676_1_4

  • Add a new container component to the auth lib

nx generate @nrwl/angular:component containers/login --project=auth

The output will look like this:

>nx generate @nrwl/angular:component containers/login --project=auth
CREATE libs/auth/src/lib/containers/login/login.component.html (20 bytes)
CREATE libs/auth/src/lib/containers/login/login.component.spec.ts (621 bytes)
CREATE libs/auth/src/lib/containers/login/login.component.ts (276 bytes)
CREATE libs/auth/src/lib/containers/login/login.component.css (0 bytes)
UPDATE libs/auth/src/lib/auth.module.ts (372 bytes)
  • Add a new presentational component to the auth lib

nx generate @nrwl/angular:component components/login-form --project=auth

Note: You may decide not to make this a presentational component but it makes it easier to test and refactor.

3. Add a default route to the auth module

libs/auth/src/auth.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Route } from '@angular/router';
import { LoginComponent } from './containers/login/login.component';
import { LoginFormComponent } from './components/login-form/login-form.component';

export const authRoutes: Route[] = [
  { path: 'login', component: LoginComponent }
];
@NgModule({
  imports: [CommonModule, RouterModule],
  declarations: [LoginComponent, LoginFormComponent]
})
export class AuthModule {}

4. Update the consuming Customer Portal App module

  • Delete everything but the router-outlet on the apps app.component.html file.

apps/customer-portal/src/app/app.component.html
<router-outlet></router-outlet>
  • Add the Auth module to the main App Module

apps/customer-portal/src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { NxModule } from '@nrwl/nx';
import { RouterModule } from '@angular/router';
import { authRoutes, AuthModule } from '@demo-app/auth';  //added

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    NxModule.forRoot(),
    RouterModule.forRoot([{path: 'auth', children: authRoutes}], { initialNavigation: 'enabled' }),
    AuthModule     // added
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

5. Add presentational component to container component

  • Add the presentational component to the container component.

libs/auth/src/lib/containers/login/login.component.html
<app-login-form (submit)="login($event)"></app-login-form>
  • Add login function to container component class

libs/auth/src/lib/containers/login/login.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
  constructor() {}

  ngOnInit() {}

  login(authenticate:any) {
    console.log(authenticate);
  }
}

6. Add new folder for shared interfaces

  • Generate a new library using the nx cli.

nx generate @nrwl/angular:lib data-models --force:true
  • Add a 'authenticate.d.ts' file to the lib folder and export the added data models from the data-models.module.ts file

libs\data-models\src\lib\authenticate.d.ts
export interface Authenticate {
    username: string;
    password: string;
}
  • Export the interface

libs\data-models\src\lib\data-models.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
export { Authenticate } from './authenticate';

@NgModule({
  imports: [CommonModule],
})
export class DataModelsModule {}
  • Add basic login form to presentational component

libs/auth/src/lib/components/login-form/login-form.component.html
<input #username placeholder="username" type="text">
<input #password placeholder="password" type="text">
<button (click)="login({username: username.value, password: password.value})">Login</button>
  • Add an angular @Output to emit the event of a form submission

libs/auth/src/lib/components/login-form/login-form.component.ts
import { Component, EventEmitter, Output } from '@angular/core';
import { Authenticate } from '@demo-app/data-models';
@Component({
  selector: 'app-login-form',
  templateUrl: './login-form.component.html',
  styleUrls: ['./login-form.component.scss']
})
export class LoginFormComponent {
  @Output() submit = new EventEmitter<Authenticate>();

  login(authenticate: Authenticate) {
    this.submit.emit(authenticate);
  }
}

7. Change the ChangeDetectionStrategy to OnPush

  • Now that we are using the presentation and container component pattern and we know that we only need to check the child components for changes if a DOM event or a @Input or @Output passes new primitives or reference values. In this way we can tell Angular not check the whole component tree which can cause performance issues in larger applications.

libs/auth/src/lib/containers/login/login.component.ts
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class LoginComponent implements OnInit {
  constructor() {}

  ngOnInit() {}

  login(authenticate: any) {
    console.log(authenticate);
  }
}

Last updated