4.3. Develop full stack boilerplate: create basic app сomponents and set up global styles structure on Angular frontend

Let’s continue developing step-by-step JavaScript full stack real world business system for transportation logistics (full table of contents here). Now we can move from backend development to frontend development . This article describes the implementation of two tasks from the first stage of the project plan  for developing the ‘Awesome Trucks Company System’:

  1. Creating basic app сomponents
  2. Setting up a global styles structure

to create main app toolbar and navigation, prepare for frontend authentication development and achieve the result shown in the mockup below:

The implementation of this mockup started from installation of Angular Material library

npm i --save @angular/material

and then install angular material fonts

npm i @material-design-icons/font

Generate scss library for styles configuration

npx nx g @nx/angular:library scss \
--prefix nav \
--importPath=@nav/frontend/ui-style \
--tags type:ui,scope:frontend \
--directory=libs/frontend/ui/ui-style \
--unitTestRunner=none \
--projectNameAndRootFormat=as-provided

And create a basic structure for styles folders and files:

And set up libs/frontend/ui-style/src/lib/scss/style.scss

@charset 'utf-8';
@import 'vendors';
@import 'abstract';
@import 'base';
@import 'components';
@import 'layout';

add style libraries to apps/frontend/project.json

"styles": [          
  "apps/frontend/src/styles.scss",
  "node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
  "node_modules/@material-design-icons/font/index.css"
  ],
"stylePreprocessorOptions": {
  "includePaths": ["libs/frontend/ui/ui-style/src/lib/scss"]          
},

and update apps/frontend/src/styles.scss

@import "style";

finally update libs/frontend/ui/ui-style/src/lib/scss/base/_global.scss

html, body { 
  height: 100%; 
  margin: 0px;
}
body { 
  font-family: Roboto, "Helvetica Neue", sans-serif;  
  *{    
    margin: 0px;
    padding: 0;
    outline: none;
    border: none;
    box-sizing: border-box;
  }
}

Now we are ready to generate basic ui components

Generate Home component:

npx nx g @nx/angular:component home \
--prefix mtfs \
--directory=libs/frontend/ui/ui-components/src/lib/home \
--style=scss \
--nameAndDirectoryFormat=as-provided

update apps/frontend/src/app/app.routes.ts to add home-component route

import { Route } from '@angular/router';
export const appRoutes: Route[] = [
  {
    path: '',
    async loadComponent() {
      const c = await import('@nav/frontend/ui-components');
      return c.HomeComponent
    }
  }
];

Generate Header component:

npx nx g @nx/angular:component header \
--prefix mtfs \
--directory=libs/frontend/ui/ui-components/src/lib/header \
--style=scss \
--nameAndDirectoryFormat=as-provided

Update HeaderComponent to add Angular Material  modules and methods for control buttons:

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import {MatToolbarModule} from '@angular/material/toolbar';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import {MatTooltipModule} from '@angular/material/tooltip';
import { MatMenuModule } from '@angular/material/menu';
import { SideNavService } from '@mtfs/frontend/utils';

@Component({
  selector: 'mtfs-header',
  standalone: true,
  imports: [
    CommonModule,
    MatToolbarModule,
    MatButtonModule,
    MatIconModule,
    MatMenuModule,
    MatTooltipModule  
  ],
  templateUrl: './header.component.html',
  styleUrl: './header.component.scss',
})
export class HeaderComponent {
  constructor(
    private sidenav: SideNavService,
  ){}
  logout(){
    console.log('logout');
  }

  toggleMenu(){
    console.log("toggle")
    this.sidenav.toggle();
  }

  displayProfile(){
    console.log('displayProfile');
  }
}

Update HeaderComponent template :

<mat-toolbar class="mat-elevation header" color="primary">
  <!--side navigation toggle button-->
    <button mat-icon-button matTooltip="Menu" color="white" aria-label="Main menu"
      (click)="clickMenu()" >
      <mat-icon aria-hidden="false" fontIcon="menu">menu</mat-icon>
    </button>
    <!--app name or logo-->
    <span class="logo">Awesome Trucks Company</span>
    <span class="example-spacer"></span>
    <!--user's profile button-->
    <button mat-icon-button matTooltip="User's profile" color="white" aria-label="User's profile" 
      class="material-symbol-outlined" (click)="displayProfile()">  
      <mat-icon aria-hidden="false" fontIcon="person">person</mat-icon>
    </button>
    <!--logout button-->
    <button mat-icon-button matTooltip="Logout" color="white" aria-label="Main menu" (click)="logout()">
      <mat-icon aria-hidden="false" fontIcon="logout">logout</mat-icon>
    </button>
  </mat-toolbar>

Create SideNavService to open and close side navigation menu by clicking menu button from header toolbar

generate libs/frontend/utils/ui library

npx nx g @nx/angular:library services \
--prefix mtfs \
--importPath=@mtfs/frontend/utils \
--tags type:utils,scope:frontend \
--directory=libs/frontend/utils \
--projectNameAndRootFormat=as-provided

generate SideNavService in libs/frontend/utils/ui library

npx nx generate @schematics/angular:service \
--name=side-nav --project=frontend \
--path=libs/frontend/utils/src/lib/services \
--no-interactive --dry-run

update SideNavService

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class SideNavService {  

  public sideNaveToggleSubject: BehaviorSubject<any> = new BehaviorSubject(null);

  public toggle(){   
    return this.sideNaveToggleSubject.next(null);
  }
}

update HeaderComponent libs/frontend/ui-components/src/lib/header/header.component.ts to add toggleMenu()

toggleMenu(){
  this.sidenav.toggle();
}

update update HomeComponent

import { Component, OnInit, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import {MatSidenavModule} from '@angular/material/sidenav';
import { MatSidenav } from '@angular/material/sidenav';
import { SideNavService } from '@mtfs/frontend/utils';

@Component({
  selector: 'mtfs-home',
  standalone: true,
  imports: [
    CommonModule,
    MatSidenavModule],
  templateUrl: './home.component.html',
  styleUrl: './home.component.scss',
})
export class HomeComponent implements OnInit {
  
  @ViewChild('sidenav', {static:true}) public sidenav!: MatSidenav;
  constructor(
    private sideNavService: SideNavService
  ){}

  ngOnInit(){
    this.sideNavService.sideNaveToggleSubject.subscribe(()=>{
      this.sidenav.toggle();
    })
  }
}

update HomeComponent template

<mat-sidenav-container class="container">
  <mat-sidenav #sidenav mode="side">    
    <mat-nav-list>
    </mat-nav-list>
  </mat-sidenav>
  <mat-sidenav-content>   
    <div>
      <router-outlet> </router-outlet>
    </div>
  </mat-sidenav-content>
</mat-sidenav-container>

In accordance with project decisions, our system will use role-based access to information within the framework of a single monolithic frontend application. Therefore, a side navigation menu will be created for each role. As part of the full stack template, navigation panels for User and Admin roles will be sufficient

generate NavigationUser component:

npx nx g @nx/angular:component navigation-user \
--prefix mtfs \
--directory=libs/frontend/ui/ui-components/src/lib/navigation-user \
--style=scss \
--nameAndDirectoryFormat=as-provided

generate NavigationAdmin component

npx nx g @nx/angular:component navigation-admin \
--prefix mtfs \
--directory=libs/frontend/ui/ui-components/src/lib/navigation-admin \
--style=scss \
--nameAndDirectoryFormat=as-provided

This components will be used further to implement Role Based Access Control

Error Message Components

generate ErrorPages library

npx nx g @nx/angular:library error-pages \
--prefix mtfs \
--importPath=@mtfs/frontend/error-pages \
--tags type:ui,scope:frontend \
--directory=libs/frontend/ui/error-pages \
--projectNameAndRootFormat=as-provided

generate NotFoundComponent

npx nx g @nx/angular:component not-found \
--prefix mtfs \
--directory=libs/frontend/ui/error-pages/src/lib/not-found \
--style=scss \
--nameAndDirectoryFormat=as-provided

generate ForbiddenComponent

npx nx g @nx/angular:component forbidden \
--prefix mtfs \
--directory=libs/frontend/ui/error-pages/src/lib/forbidden \
--style=scss \
--nameAndDirectoryFormat=as-provided

generate ServerErrorComponent

npx nx g @nx/angular:component server-error \
--prefix mtfs \
--directory=libs/frontend/ui/error-pages/src/lib/server-error \
--style=scss \
--nameAndDirectoryFormat=as-provided

delete error-page folder and update libs/frontend/ui/error-pages/src/index.ts

export * from './lib/forbidden/forbidden.component';
export * from './lib/not-found/not-found.component';
export * from './lib/server-error/server-error.component';

run frontend application to show results

nx serve frontend

And the result:

Summary and conclusions

This article describes the initial steps in creating a front-end application for building a JavaScript full stack boilerplate and for further development of the Awesome Trucks Company system. We created a set of basic user interface components, such as:

  • HomeComponent
  • HeaderComponent with toolbar
  • Navigation User component
  • Navigation Admin component

We also installed Angular Material and Material Fonts, set up SCSS files and folder structure, and created a SidNave Service to show and hide the side navigation menu panel. Additionally, we generated components for error messages such as:

  • NotFoundComponent for routes that not found
  • ForbiddenСomponent for cases when the route is not available for the current user’s role
  • ServerErrorComponent for other server errors

So, now we are ready to mark tasks Сreate basic app-components” and “Setup global styles structure as completed and proceed to create frontend cookie-based authentication.


Discover more from More Than Fullstack

Subscribe to get the latest posts sent to your email.

Leave a Comment