8 - Layout Lib and BehaviorSubjects
In this section we make a reusable Layout and a BehaviorSubject to share User state
Last updated
In this section we make a reusable Layout and a BehaviorSubject to share User state
Last updated
nx generate @nrwl/angular:lib layout
In the past, the cli would ask you to choose the stylesheet language. If this happens, make sure to choose sass. As of Nx 12, this will not happen anymore.
Add a layout container component
nx generate @nrwl/angular:component containers/layout --project=layout
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LayoutComponent } from './containers/layout/layout.component';
import { MaterialModule } from '@demo-app/material'; // Added
Note the <ng-content></ng-content> to transclude content into the component. Also note the flexLayout attribute.
<mat-toolbar color="primary" fxLayout="row">
<span>Customer Portal</span>
</mat-toolbar>
<ng-content></ng-content>
A BehaviorSubject is a special observable you can both subscribe to and pass values.
Add a BehaviorSubject to the Auth service.
import { Injectable } from '@angular/core';
import { Authenticate, User } from '@demo-app/data-models';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private userSubject$ = new BehaviorSubject<User>(null);
user$ = this.userSubject$.asObservable();
constructor(private httpClient: HttpClient) {}
login(authenticate: Authenticate): Observable<User> {
return this.httpClient
.post<User>('http://localhost:3000/login', authenticate)
.pipe(tap((user: User) => this.userSubject$.next(user)));
}
}
Re-export the AuthService from the Auth Libs index.ts file.
export * from './lib/auth.module';
export { AuthService } from './lib/services/auth/auth.service';
Add logic to subscribe to User Subject in the Auth Service
import { Component, OnInit } from '@angular/core';
import { AuthService } from '@demo-app/auth'
import { Observable } from 'rxjs';
import { User } from '@demo-app/data-models';
@Component({
selector: 'demo-app-layout',
templateUrl: './layout.component.html',
styleUrls: ['./layout.component.scss']
})
export class LayoutComponent implements OnInit {
user$: Observable<User>;
constructor(private authService: AuthService) {}
ngOnInit() {
this.user$ = this.authService.user$;
}
}
<demo-app-layout>
<router-outlet></router-outlet>
</demo-app-layout>
@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';
body {
margin: 0;
}
Add styles to the layout.component.scss
.right-nav {
margin-left: auto;
}
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';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; // Added
import { LayoutModule } from '@demo-app/layout';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
NxModule.forRoot(),
RouterModule.forRoot([{path: 'auth', children: authRoutes}], { initialNavigation: 'enabled' }),
AuthModule,
LayoutModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
We will add the products link later but for now it will error if we try and click it.
<mat-toolbar color="primary" fxLayout="row">
<span>Customer Portal</span>
<div class="right-nav">
<span *ngIf="user$ | async as user">
<button mat-button>{{(user$ | async)?.username}}</button>
<button mat-button [routerLink]="['/products']" routerLinkActive="router-link-active">Products</button>
<button mat-button>Logout</button>
</span>
<span *ngIf="!(user$ | async)">
<span mat-button [routerLink]="['/auth/login']" routerLinkActive="router-link-active">Login</span>
</span>
</div>
</mat-toolbar>
<ng-content></ng-content>
There might be some extra styles in the app.component.scss like this:
/*
* Remove template code below
*/
:host {
display: block;
font-family: sans-serif;
min-width: 300px;
max-width: 600px;
margin: 50px auto;
}
.gutter-left {
margin-left: 9px;
}
The margin: 50px line in particular will push down the entire app layout, so it's a good idea to just remove everything in this file.
Add a toolbar presentational component.
Pass user into presentational component via inputs.