Angular - Surface SharePoint List Data With MSGraph - Part 3 - Add Some Style

Share on:

Overview

This Project was discontinued mid 2021!

Leaving here for reference!

Please Note!

One of the dangers in writing a tutorial like this is that by the time you are finished writing it, there is a new revision to the libraries you are using. So please be aware that you may run into issues, that you will need to resolve yourself, if you use a different version of the libraries than what I am using.

We need to provide a way for the user to switch between light or dark mode.

We will alsosetup authentication to Azure AD and surface data contained in a SharePoint list.

This app will not use one of the Microsoft App Models. Our app is not a Provider nor SharePoint Hosted App Model. It is not a SharePoint folder nor an SPFx solution.

We will need to register our app with Azure Active Directory but we do not need to register with the SharePoint App Catalog.

Our app is a standalone app hosted on our own server and uses SharePoint as a data source, we can connect with any other data source we choose.

Check with your companies policies, this app could still be considered as what is known as a side loaded app.

Let's begin with package.json

Install

  • @angular/cdk
  • @angular/flex-layout
  • @angular/material

Or Copy and paste

Change:

 1{
 2  "name": "coffee-manager",
 3  "version": "0.0.0",
 4  "scripts": {
 5    "ng": "ng",
 6    "start": "ng serve",
 7    "build": "ng build",
 8    "test": "ng test",
 9    "lint": "ng lint",
10    "e2e": "ng e2e"
11  },
12  "private": true,
13  "dependencies": {
14    "@angular/animations": "~11.0.0",
15    "@angular/common": "~11.0.0",
16    "@angular/compiler": "~11.0.0",
17    "@angular/core": "~11.0.0",
18    "@angular/forms": "~11.0.0",
19    "@angular/platform-browser": "~11.0.0",
20    "@angular/platform-browser-dynamic": "~11.0.0",
21    "@angular/router": "~11.0.0",
22    "rxjs": "~6.6.0",
23    "tslib": "^2.0.0",
24    "zone.js": "~0.10.2"
25  },
26  "devDependencies": {
27    "@angular-devkit/build-angular": "~0.1100.1",
28    "@angular/cli": "~11.0.1",
29    "@angular/compiler-cli": "~11.0.0",
30    "@types/jasmine": "~3.6.0",
31    "@types/node": "^12.11.1",
32    "codelyzer": "^6.0.0",
33    "jasmine-core": "~3.6.0",
34    "jasmine-spec-reporter": "~5.0.0",
35    "karma": "~5.1.0",
36    "karma-chrome-launcher": "~3.1.0",
37    "karma-coverage": "~2.0.3",
38    "karma-jasmine": "~4.0.0",
39    "karma-jasmine-html-reporter": "^1.5.0",
40    "protractor": "~7.0.0",
41    "ts-node": "~8.3.0",
42    "tslint": "~6.1.0",
43    "typescript": "~4.0.2"
44  }
45}

To:

 1{
 2  "name": "coffee-manager",
 3  "version": "0.0.0",
 4  "scripts": {
 5    "ng": "ng",
 6    "start": "ng serve -o",
 7    "build": "ng build",
 8    "test": "ng test",
 9    "lint": "ng lint",
10    "e2e": "ng e2e"
11  },
12  "private": true,
13  "dependencies": {
14    "@angular/animations": "^11.0.4",
15    "@angular/cdk": "^11.0.2",
16    "@angular/common": "^11.0.4",
17    "@angular/compiler": "^11.0.4",
18    "@angular/core": "^11.0.4",
19    "@angular/flex-layout": "^11.0.0-beta.33",
20    "@angular/forms": "^11.0.4",
21    "@angular/material": "^11.0.2",
22    "@angular/platform-browser": "^11.0.4",
23    "@angular/platform-browser-dynamic": "^11.0.4",
24    "@angular/router": "^11.0.4",
25    "rxjs": "~6.6.0",
26    "tslib": "^2.0.0",
27    "zone.js": "~0.10.2"
28  },
29  "devDependencies": {
30    "@angular-devkit/build-angular": "^0.1100.4",
31    "@angular/cli": "^11.0.4",
32    "@angular/compiler-cli": "^11.0.4",
33    "@types/jasmine": "~3.6.0",
34    "@types/node": "^12.19.9",
35    "codelyzer": "^6.0.0",
36    "jasmine-core": "~3.6.0",
37    "jasmine-spec-reporter": "~5.0.0",
38    "karma": "~5.1.0",
39    "karma-chrome-launcher": "~3.1.0",
40    "karma-coverage": "~2.0.3",
41    "karma-jasmine": "~4.0.0",
42    "karma-jasmine-html-reporter": "^1.5.0",
43    "protractor": "~7.0.0",
44    "ts-node": "~8.3.0",
45    "tslint": "~6.1.0",
46    "typescript": "~4.0.2"
47  }
48}

Then type npm i or npm install in the same dir as package.json

Close package.json

We need to add some resources to our index.html file.

Change:

 1<!doctype html>
 2<html lang="en">
 3<head>
 4  <meta charset="utf-8">
 5  <title>Coffee Manager</title>
 6  <base href="/">
 7  <meta name="viewport" content="width=device-width, initial-scale=1">
 8  <link rel="icon" type="image/x-icon" href="favicon.ico">
 9</head>
10<body>
11  <app-root></app-root>
12</body>
13</html>

To:

 1<!doctype html>
 2<html lang="en">
 3<head>
 4  <meta charset="utf-8">
 5  <title>Coffee Manager</title>
 6  <base href="/">
 7  <meta name="viewport" content="width=device-width, initial-scale=1">
 8  <link rel="icon" type="image/x-icon" href="favicon.ico">
 9  <link type="css" rel="stylesheet" href="styles.css" />
10
11  <link
12    href="https://fonts.googleapis.com/css?family=Raleway:300,400,500,600&display=swap"
13    rel="stylesheet"
14  />
15  <link
16    href="https://fonts.googleapis.com/icon?family=Material+Icons"
17    rel="stylesheet"
18  />
19</head>
20<body class="mat-typography">
21  <app-root></app-root>
22</body>
23</html>

Notice the new style class, mat-typography, on the body. We are now making use of Google Material Design styles.

We will use the default style sheet for our informational components i.e. messages, html, body and the logo.

Add A Theme

We will create a second stylesheet for our theme styles.

Let's change the default styles.scss file now.

Add the following to styles.scss

 1/* You can add global styles to this file, and also import other style files */
 2
 3
 4
 5html, body { height: 100%; }
 6body { margin: 0; font-family: Raleway, "Helvetica Neue", sans-serif; }
 7
 8.success.mat-snack-bar-container {
 9  background-color: rgba(132,189,0, .93); // #84BD00;
10  color:white;
11}
12
13.error.mat-snack-bar-container {
14  background-color: rgba(210,38,48, .93); //#D22630
15  color:white;
16}
17
18.warn.mat-snack-bar-container {
19  background-color: rgba(232,119,34, .93); //#E87722
20  color:white;
21}
22
23.info.mat-snack-bar-container {
24  background-color: rgba(0,181,226, .93); //#00B5E2
25  color:white;
26}
27
28.logo {
29  position: relative;
30  left: 0px;
31  background-color: white;
32  border: solid;
33  border-color: gainsboro;
34  border-width: 0 0 .1px 0;
35}

Next create a file in the src directory called app-theme.scss

Add the following to app-theme.scss

 1/* You can add global styles to this file, and also import other style files */
 2@import '~@angular/material/theming';
 3
 4@import url('https://fonts.googleapis.com/css?family=Raleway:300,400,500,600');
 5
 6// Plus imports for other components in your app.
 7
 8// Include the common styles for Angular Material. We include this here so that you only
 9// have to load a single css file for Angular Material in your app.
10// **Be sure that you only ever include this mixin once!**
11@include mat-core();
12
13
14
15$mdc-typography-font-family: unquote("Raleway, sans-serif");
16
17// $mdc-theme-primary: #6200ee;
18// $mdc-theme-secondary: #018786;
19
20// Define the default theme (same as the example above).
21$candy-app-primary: mat-palette($mat-indigo);
22$candy-app-accent:  mat-palette($mat-orange, A200, A100, A400);
23$candy-app-warn: mat-palette($mat-red);
24
25$candy-app-theme:   mat-light-theme(
26    $candy-app-primary, $candy-app-accent, $candy-app-warn);
27
28
29
30// Include the default theme styles (color and default density)
31@include angular-material-theme($candy-app-theme);
32
33
34// Include the dark color styles inside of a block with a CSS class. You can make this
35// CSS class whatever you want. In this example, any component inside of an element with
36// `.unicorn-dark-theme` will be affected by this alternate dark theme instead of the default theme.
37.dark-theme {
38    // Define an alternate dark theme.
39$primary-dark: mat-palette($mat-blue-grey);
40$accent-dark:  mat-palette($mat-orange, A200, A100, A400);
41$warn-dark:    mat-palette($mat-deep-orange);
42
43$theme-dark:   mat-dark-theme( $primary-dark, $accent-dark, $warn-dark);
44
45
46@include angular-material-color($theme-dark);
47}

Next

We need to tell our app about our new theme!

Open angular.json

Find

 1"architect": {
 2        "build": {
 3          "builder": "@angular-devkit/build-angular:browser",
 4          "options": {
 5            "outputPath": "dist/coffeeManager",
 6            "index": "src/index.html",
 7            "main": "src/main.ts",
 8            "polyfills": "src/polyfills.ts",
 9            "tsConfig": "tsconfig.app.json",
10            "aot": true,
11            "assets": [
12              "src/favicon.ico",
13              "src/assets"
14            ],
15            "styles": [
16              "src/styles.scss"
17            ],
18            "scripts": []
19          },

Add "src/app-theme.scss" after "src/styles.scss"

 1"architect": {
 2        "build": {
 3          "builder": "@angular-devkit/build-angular:browser",
 4          "options": {
 5            "outputPath": "dist/coffeeManager",
 6            "index": "src/index.html",
 7            "main": "src/main.ts",
 8            "polyfills": "src/polyfills.ts",
 9            "tsConfig": "tsconfig.app.json",
10            "aot": true,
11            "assets": [
12              "src/favicon.ico",
13              "src/assets"
14            ],
15            "styles": [
16              "src/styles.scss",
17              "src/app-theme.scss"
18            ],
19            "scripts": []
20          },

Be sure to add "src/app-theme.scss" after "src/styles.scss" in the second location under the "test" node.

We still have some work to do before our app will look like the screenshots above.

Create a Shared Module

In the src/app dir create a new folder named shared

In the shared folder create a new file named material.module.ts

Add the following to material.module.ts

 1import { NgModule } from '@angular/core';
 2
 3import { MatAutocompleteModule } from '@angular/material/autocomplete';
 4import { MatCheckboxModule } from '@angular/material/checkbox';
 5import { MatDatepickerModule } from '@angular/material/datepicker';
 6import { MatNativeDateModule } from '@angular/material/core'
 7import { MatFormFieldModule } from '@angular/material/form-field';
 8import { MatInputModule } from '@angular/material/input';
 9import { MatRadioModule } from '@angular/material/radio';
10import { MatSelectModule } from '@angular/material/select';
11import { MatSliderModule } from '@angular/material/slider';
12import { MatSlideToggleModule } from '@angular/material/slide-toggle';
13import { MatMenuModule } from '@angular/material/menu';
14import { MatSidenavModule } from '@angular/material/sidenav';
15import { MatToolbarModule } from '@angular/material/toolbar';
16import { MatCardModule } from '@angular/material/card';
17import { MatDividerModule } from '@angular/material/divider';
18import { MatExpansionModule } from '@angular/material/expansion';
19import { MatGridListModule } from '@angular/material/grid-list';
20import { MatListModule } from '@angular/material/list';
21import { MatStepperModule } from '@angular/material/stepper';
22import { MatTabsModule } from '@angular/material/tabs';
23import { MatTreeModule } from '@angular/material/tree';
24import { MatButtonModule } from '@angular/material/button';
25import { MatButtonToggleModule } from '@angular/material/button-toggle';
26import { MatBadgeModule } from '@angular/material/badge';
27import { MatChipsModule } from '@angular/material/chips';
28import { MatIconModule } from '@angular/material/icon';
29import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
30import { MatProgressBarModule } from '@angular/material/progress-bar';
31import { MatRippleModule } from '@angular/material/core';
32import { MatBottomSheetModule } from '@angular/material/bottom-sheet';
33import { MatDialogModule } from '@angular/material/dialog';
34import { MatSnackBarModule } from '@angular/material/snack-bar';
35import { MatTooltipModule } from '@angular/material/tooltip';
36import { MatPaginatorModule } from '@angular/material/paginator';
37import { MatSortModule } from '@angular/material/sort';
38import { MatTableModule } from '@angular/material/table';
39
40
41@NgModule({
42  declarations: [],
43  exports: [
44    MatAutocompleteModule,
45    MatCheckboxModule,
46    MatDatepickerModule,
47    MatNativeDateModule,
48    MatFormFieldModule,
49    MatInputModule,
50    MatRadioModule,
51    MatSelectModule,
52    MatSliderModule,
53    MatSlideToggleModule,
54    MatMenuModule,
55    MatSidenavModule,
56    MatToolbarModule,
57    MatCardModule,
58    MatDividerModule,
59    MatExpansionModule,
60    MatGridListModule,
61    MatListModule,
62    MatStepperModule,
63    MatTabsModule,
64    MatTreeModule,
65    MatButtonModule,
66    MatButtonToggleModule,
67    MatBadgeModule,
68    MatChipsModule,
69    MatIconModule,
70    MatProgressSpinnerModule,
71    MatProgressBarModule,
72    MatRippleModule,
73    MatBottomSheetModule,
74    MatDialogModule,
75    MatSnackBarModule,
76    MatTooltipModule,
77    MatPaginatorModule,
78    MatSortModule,
79    MatTableModule
80  ]
81})
82export class MaterialModule { }

Save and close material.module.ts

Now we need to tell our app.module.ts where to find our new shared module.

In app.module.ts add the following in the imports section,

1import { MaterialModule } from './shared/material.module';
2import { FlexLayoutModule } from '@angular/flex-layout';

Then under the @NgModule add MaterialModule and the FlexLayoutModule to the imports array.

Save and close app.module.ts

Open app.component.scss and add the following

1.main {
2  flex: 1;
3  width: 100%;
4  min-width: 100%;
5
6  height: 100%;
7  min-height: 100%;
8
9}

Save and close app.component.scss

Open app.component.html and replace it's contents with

1<main role="main" class="main">
2  <app-sidenav></app-sidenav>
3</main>

Save and close app.component.html

Let's Create a Side Nav Component

Create a new folder called core in src/app

Create another folder in src/app/core called sidenav

Switch to the command line, if the app is running, stop it with

ctrl + c

We will create the sidenav component from the command line, this will also add it to our app.module.ts for us automatically.

On the command type

ng g c -d sidenav --style scss

Notice the -d flag, this allows us to run the command with out actually creating anything, this let's us make sure everything will be created correctly.

We are also adding a style sheet for the sidenav.

Now if you run the command, you should see the following on the command line.

1keith@Keiths-MacBook-Pro coffeeManager % ng g c -d sidenav --style scss 
2CREATE src/app/sidenav/sidenav.component.scss (0 bytes)
3CREATE src/app/sidenav/sidenav.component.html (22 bytes)
4CREATE src/app/sidenav/sidenav.component.spec.ts (633 bytes)
5CREATE src/app/sidenav/sidenav.component.ts (280 bytes)
6UPDATE src/app/app.module.ts (639 bytes)
7
8NOTE: The "dryRun" flag means no changes were made.

Uh! Oh! I just noticed that the sidenav would have been created in the wrong folder. Good thing this is a dry run.

Let's change the command to

ng g c -d core/sidenav --style scss

Run it once more

1keith@Keiths-MacBook-Pro coffeeManager % ng g c -d core/sidenav --style scss
2CREATE src/app/core/sidenav/sidenav.component.scss (0 bytes)
3CREATE src/app/core/sidenav/sidenav.component.html (22 bytes)
4CREATE src/app/core/sidenav/sidenav.component.spec.ts (633 bytes)
5CREATE src/app/core/sidenav/sidenav.component.ts (280 bytes)
6UPDATE src/app/app.module.ts (644 bytes)
7
8NOTE: The "dryRun" flag means no changes were made.

If everything looks good, remove the -d flag and run the command again. This time the files will be created and the sidenav.component.ts will be registered in our app.module.ts file for us.

Run the app once more and you will see the following.

"Coffee Manager - Sidenav- First Run"

What The! All this work, Just for This!

Hold On! Hold On! It get's better!

Trust me I'm a professional!

Stop the app from running by typing

ctrl + c

Open sidenav.component.html and replace it's contents with

 1<mat-sidenav-container
 2  class="app-sidenav-container"
 3  [class.dark-theme]="isDarkTheme">
 4  <mat-sidenav
 5    #sidenav
 6    class="app-sidenav mat-elevation-z10"
 7    [opened]="!isScreenSmall"
 8    [mode]="isScreenSmall ? 'over' : 'side'"
 9  >
10    <mat-toolbar class="logo"><span><img src="../../../assets/logo.png" width="150px" height="150px"/></span></mat-toolbar>
11    <mat-list role="list">
12      <mat-list-item class="app-sidenav-navitem" role="listitem"><button mat-icon-button [routerLink]="['/home']" aria-label="Incident Reports">
13        <mat-icon>house</mat-icon> Home
14      </button></mat-list-item>
15      <mat-list-item role="listitem"><button mat-icon-button [routerLink]="['/brands']" aria-label="Brands">
16        <mat-icon>local_cafe</mat-icon> Brands
17      </button></mat-list-item>
18      <mat-list-item role="listitem"><button mat-icon-button [routerLink]="['/countries']" aria-label="Countries">
19        <mat-icon>flag</mat-icon> Countries
20      </button></mat-list-item>
21      <mat-list-item role="listitem"><button mat-icon-button [routerLink]="['/apparel']" aria-label="Apparel">
22        <mat-icon>shopping_bag</mat-icon> Apparel
23      </button></mat-list-item>
24      <mat-list-item role="listitem"><button mat-icon-button [routerLink]="['/gifts']" aria-label="Gifts">
25        <mat-icon>card_giftcard</mat-icon> Gift Cards
26      </button></mat-list-item>
27      <mat-list-item role="listitem"><button mat-icon-button [routerLink]="['/about']" aria-label="About Coffee Manager">
28        <mat-icon>topic</mat-icon> About
29      </button></mat-list-item>
30    </mat-list>
31  </mat-sidenav>
32
33  <div class="app-sidenav-content">
34    <app-toolbar
35      (toggleTheme)="toggleTheme()"
36      (toggleSidenav)="sidenav.toggle()"
37    ></app-toolbar>
38    <div class="wrapper">
39      <router-outlet></router-outlet>
40    </div>
41  </div>
42</mat-sidenav-container>

Open sidenav.component.scss and replace it's contents with

 1.app-sidenav-container {
 2  flex: 1;
 3  width: 100%;
 4  min-width: 100%;
 5  height: 100%;
 6  min-height: 100%;
 7}
 8
 9.app-sidenav-content {
10  display: flex;
11  height: 100%;
12  flex-direction: column;
13
14}
15
16.mat-icon-button {
17  text-align: left;
18  width: 120px;
19}
20
21.app-sidenav {
22  width: 200px;
23}
24
25.wrapper {
26  margin: 5px;
27}

Create the Toobar component

Just as we did with our sidenav component, we first do a dry run on our toolbar component.

ng g c -d core/toolbar --style scss

1keith@Keiths-MacBook-Pro coffeeManager % ng g c -d core/toolbar --style scss
2CREATE src/app/core/toolbar/toolbar.component.scss (0 bytes)
3CREATE src/app/core/toolbar/toolbar.component.html (22 bytes)
4CREATE src/app/core/toolbar/toolbar.component.spec.ts (633 bytes)
5CREATE src/app/core/toolbar/toolbar.component.ts (280 bytes)
6UPDATE src/app/app.module.ts (735 bytes)
7
8NOTE: The "dryRun" flag means no changes were made.

If everything looks good, we remove the -d flag to create our toolbar.

Replace the contents of toolbar.component.scss with

 1
 2$iconWidth: 56px;
 3
 4.toolbar-spacer {
 5  flex: 1 1 auto;
 6}
 7
 8.sidenav-toggle {
 9  display: none;
10
11  padding: 0;
12  margin: 8px;
13  min-width: $iconWidth;
14
15  @media (max-width: 720px) {
16    display: flex;
17    align-items: center;
18    justify-content: center;
19  }
20}
21
22
23mat-icon {
24  font-size: 30px;
25  height: $iconWidth;
26  width: $iconWidth;
27  line-height: $iconWidth;
28  color: white;
29}

Replace the contents of toolbar.component.html with

 1<mat-toolbar color="primary">
 2  <button mat-button class="sidenav-toggle" (click)="toggleSidenav.emit()">
 3    <mat-icon>menu</mat-icon>
 4  </button>
 5
 6  <span>Coffee Manager</span>
 7  <span class="toolbar-spacer"></span>
 8
 9  &nbsp;
10  <span>
11
12</span>
13
14  <button mat-button [matMenuTriggerFor]="menu">
15    <mat-icon>more_vert</mat-icon>
16  </button>
17
18
19  <mat-menu #menu="matMenu">
20    <button mat-menu-item (click)="toggleTheme.emit()">Toggle theme</button>
21  </mat-menu>
22</mat-toolbar>

Open toolbar.component.ts and add change the following.

 1import { Component, OnInit } from '@angular/core';
 2
 3@Component({
 4  selector: 'app-toolbar',
 5  templateUrl: './toolbar.component.html',
 6  styleUrls: ['./toolbar.component.scss']
 7})
 8export class ToolbarComponent implements OnInit {
 9
10  
11  constructor() { }
12
13  ngOnInit(): void {
14  }
15
16}

To

 1import { Component, EventEmitter, Output } from '@angular/core';
 2
 3
 4
 5@Component({
 6  selector: 'app-toolbar',
 7  templateUrl: './toolbar.component.html',
 8  styleUrls: ['./toolbar.component.scss']
 9})
10export class ToolbarComponent{
11
12  @Output() toggleDir = new EventEmitter<void>();
13
14
15}

Save and close toolbar.component.ts and toolbar.component.html and toolbar.component.scss

Open sidenav.component.ts and replace it's contents with

 1import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout';
 2import { Component, OnInit, ViewChild } from '@angular/core';
 3import { MatSidenav } from '@angular/material/sidenav';
 4
 5const SMALL_WIDTH_BREAKPOINT = 720;
 6
 7@Component({
 8  selector: 'app-sidenav',
 9  templateUrl: './sidenav.component.html',
10  styleUrls: ['./sidenav.component.scss']
11})
12export class SidenavComponent implements OnInit {
13
14  public isScreenSmall: boolean;
15
16  constructor(
17    private breakpointObserver: BreakpointObserver
18  ) { }
19
20  // users: Observable<User[]>;
21  isDarkTheme: boolean = false;
22  dir: string = 'ltr';
23
24  @ViewChild(MatSidenav) sidenav: MatSidenav;
25
26  toggleTheme() {
27    this.isDarkTheme = !this.isDarkTheme;
28  }
29
30  toggleDir() {
31    this.dir = this.dir == 'ltr' ? 'rtl' : 'ltr';
32  }
33
34  ngOnInit(): void {
35    this.breakpointObserver
36      .observe([`(max-width: ${SMALL_WIDTH_BREAKPOINT}px)`])
37      .subscribe((state: BreakpointState) => {
38        this.isScreenSmall = state.matches;
39      });
40  }
41
42}

We are telling the app that if the window size is 720 px wide then we need change the toolbar to be hidden and show the hamburger menu to toggle the toolbar. We also setup the ability for the user to switch between light mode and dark mode.

Restart the app with ng serve

You will see the error, Property 'sidenav' has no initializer and is not definitely assigned in the constructor., this is due to TypeScripts strict Class initialization. We chose to enable this when we created the app.

Thanks goes to Mikkel Christensen for resolving this issue with his stackoverflow answer

Change sidenav.component.ts to

 1import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout';
 2import { Component, OnInit, ViewChild } from '@angular/core';
 3import { MatSidenav } from '@angular/material/sidenav';
 4
 5const SMALL_WIDTH_BREAKPOINT = 720;
 6
 7@Component({
 8  selector: 'app-sidenav',
 9  templateUrl: './sidenav.component.html',
10  styleUrls: ['./sidenav.component.scss']
11})
12export class SidenavComponent implements OnInit {
13
14  public isScreenSmall!: boolean;
15
16  constructor(
17    private breakpointObserver: BreakpointObserver
18  ) { }
19
20  isDarkTheme: boolean = false;
21  dir: string = 'ltr';
22
23  @ViewChild(MatSidenav) sidenav!: MatSidenav;
24
25  toggleTheme() {
26    this.isDarkTheme = !this.isDarkTheme;
27  }
28
29  toggleDir() {
30    this.dir = this.dir == 'ltr' ? 'rtl' : 'ltr';
31  }
32
33  ngOnInit(): void {
34    this.breakpointObserver
35      .observe([`(max-width: ${SMALL_WIDTH_BREAKPOINT}px)`])
36      .subscribe((state: BreakpointState) => {
37        this.isScreenSmall = state.matches;
38      });
39  }
40
41}

If you stop and start the app once more, you see the error Property 'isDarkTheme' does not exist on type 'ToolbarComponent'.

Let's resolve this now.

Open toolbar.component.ts and change the contents to

 1import { Component, EventEmitter, Output } from '@angular/core';
 2
 3
 4@Component({
 5  selector: 'app-toolbar',
 6  templateUrl: './toolbar.component.html',
 7  styleUrls: ['./toolbar.component.scss']
 8})
 9export class ToolbarComponent{
10
11  @Output() toggleSidenav = new EventEmitter<void>();
12  @Output() toggleTheme = new EventEmitter<void>();
13  @Output() toggleDir = new EventEmitter<void>();
14
15}

Save and close.

Open app.module.ts and add the following

 1import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
 2
 3// Other code omitted for brevity
 4
 5
 6],
 7  imports: [
 8    BrowserModule,
 9    AppRoutingModule,
10    BrowserAnimationsModule,
11    MaterialModule,
12    FlexLayoutModule,

Restart the app and you should see something similar to the following:

"Coffee Manager - Styled - First Run"

None of our side menu buttons will work at this point because we have not setup routing yet, but if you click the vertical elipsis button in the toolbar, you can switch between light and dark modes.

You'll also notice in the Upper left, there is an outline of where your logo should go. Let's finish this post out by adding a logo.

Open sidenav.component.html and change the src attribute to point to the location of your logo.

1 <mat-toolbar class="logo"><span><img src="../../../assets/logo.png" width="150px" height="150px"/></span></mat-toolbar>

"Coffee Manager - with Logo"

Next - Part: 4 - Authentication