Blog

How To Add Angular Material To An Angular Project

Blog Post #009

Duncan Faulkner – August 2020

Adding Angular Material to a new or existing Angular project is a simple process. In the terminal, change the directory to your project and type:

ng add @angular/material

The Angular CLI will now install Angular Material, Angular CDK and Angular Animations and add these as a dependency to the project.

In previous versions of Angular (before v9) hammerjs was required for touch and gesture support.

npm install hammerjs

In the main.ts file in the imports at the top, include the following:

import 'hammerjs' // add this import

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.log(err));

Starting with version 9 of Angular hammerjs is now optional, you no longer need to install this as a dependency, it is now part of @angular/platform-browser. To use it, add the HammerModule to the platform-browser import in the app.module.ts file and include it in the NgModule imports array.

import { HammerModule, BrowserModule } from '@angular/platform-browser';

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserAnimationsModule
    BrowserModule,
    HammerModule,
  ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

You can also create a custom class to override specific features of HammerModule.

import { Injectable } from '@angular/core';
import { BrowserModule, 
         HammerModule, 
         HammerGestureConfig, 
         HAMMER_GESTURE_CONFIG} 
from '@angular/platform-browser';

@Injectable()
export class HammerConfig extends HammerGestureConfig {
    overrides = <any> { 
       'pinch': {enable: true}
  }
}

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserAnimationsModule
    BrowserModule,
    HammerModule,
  ],
    providers: 
    [{
      provide: HAMMER_GESTURE_CONFIG, 
      useClass: HammerConfiguration
   }],
   bootstrap: [AppComponent]
})
export class AppModule {
}

The other values that can be overridden are:
pan, pinch, press, rotate, swipe and tap.
Note: that pinch and rotate are disabled by default.

Thanks for reading enjoy…

Follow me on Twitter to get notified about my newest blog posts!🐥


How To Add SSL Certificates To Azure App Service

Photo by Dayne Topkin on Unsplash

Blog Post #008

Duncan Faulkner – February 2020

Back in 2018, Scott Hanselman wrote this post on adding Let’s Encrypt SSL certificates to Azure App Services, click here to see Scott’s original post.

I’ve used Scott’s post to apply SSL certificates to my websites for a while now, but the Azure interface keeps changing and Scott’s post is difficult to follow because it has so changed so much. So while I was adding another SSL certificate, I thought I would write an update post as a reminder to myself but also to help others who find themselves trying to add an SSL certificate to Azure using the Lets Encrypt extension.

This post assumes you have a website setup and running and a domain name, this post just walks you through adding a Lets Encrypt certificate to the website.

Checklist

Azure Storage connection string – You’ll need one for the extension to store state. If you haven’t got one setup configure this before you start.

App Service Hosting Plan and App Service Resource Group Name – your Azure plan (the VM your site runs on) and your site must be in the same resource group.

There are a number of ID’s and connection strings to copy and paste that will be required at the end, I suggest opening a text editor to copy these into as we go.

So open Azure and Login.

App Registration

From the Azure home page, select the App registrations menu item.

From the App Registrations page, select New Registration.

For the Name, my convention is to use: Let’s Encrypt – <website name>, where website name is the name of my website I’m adding the SSL to. Select your supported account type, for me this was the default and for the Redirect URI, select web and enter the full URL to your website.

Open the newly created resource, and copy to your text editor the Application (client) ID, Directory (tenant) ID, Object ID as you will need these later in the process.

In Certificates and Secrets, create a New Client Secret, set the Description to Login and set it to Never Expire. Before closing this page copy the value to your text editor for later.

Now go to the Resource Group, this is the same resource group that your website belongs to, I’ve tried having a different resource group and couldn’t get it to work.

Once in the Resource Group, select Access Control (IAM) from the menu, click Add a new Role Assignment, in the Role drop down select Contributor, in the Assign access to drop down select Azure AD User, group, or service Principal (the default) and in the Select drop down type in Let’s Encrypt – website name this refers to the App Registration from earlier. The Selected Members should now show the Let’s Encrypt website name, now click Save.

Now head over to your actual App Service (your website) and click Extensions, Click Add Extension, then Select Choose Extension. In the list of extensions, scroll down until you find Azure Let’s Encrypt SKJP, there are two versions select the first one (see image below).

Then accept the Terms and Conditions and click OK that to return to the App Service. The extension should now be installed.

Scroll back up to Configuration and in the Application Settings section add two Connection strings. These are AzureWebJobsDashboard and AzureWebJobsStorage – Don’t forget this step or it will work once but won’t renew in three months during the renewal.

Both of these should be set to your Azure Storage Account connection string, e.g. DefaultEndpointsProtocol=https;AccountName=[myaccount];AccountKey=[mykey];

Replace value with your Azure Storage Account connection string details.

Save the changes.

And that’s pretty much it for the Azure side of things, now we need to configure the Extension.

In a new browser tab enter: http://YOURSITENAME.scm.azurewebsites.net/LetsEncrypt into the address bar, replacing YOURSITENAME with your actual website name.

This can take a while to resolve, so be patient. Once resolved you should see this.

Scroll down to the Automated Installation section.

Starting from the top.

Tenant – this is your Azure Active Directory URL, its the one ending xxxxx.onmicrosoft.com

Subscription ID – this can be found in the Overview page of your website.

Client ID – this is the application (client) ID from the App Registration section.

Client Secret – this is the value of the Login from the Certificates and Secrets section.

Resource Group Name – is the name of the Resource group

Service Plan Resource Group Name – this is the resource group your service plan is in.

Note: Resource Group Name and Service Plan Resource Group Name are usually the same.
I tried to have a different resource group and service plan resource group, but I couldn’t get it to work. As soon as I moved them into the same resource group it all worked. I missed this when reading Scott’s blog post the first time, (it’s in the checklist section if you are wondering).

Use IP Based SSL – I’ve left this unchecked.

Web App Name – this will be pre-populated with your website name.

Site Slot name – I’ve left this blank.

Dashboard Connection String – this will be pre-populated from your connection string set earlier.

Storage Connection String – this will be pre-populated from your Azure Account Storage connection string.

Update Application Settings and Virtual Directory (if needed) – I’ve checked this box, this will update your site in Azure.


With all that information in place apply the settings, this will take a while to complete, so please be patient.

When this (eventually) returns it will list your website domain and any previous SSL certificates you may have. Click Next.

Select all the hostnames you want an SSL for (you can of course just select one domain as a test), enter a valid email address, as this is the first time creating an SSL for this website, I would recommend checking the staging check box. By using staging it allows you to generate unlimited test SSL certificates, when unchecked the limit is five and you will have to wait a whole month before you can generate any more (only made that mistake once). Just in case you have an issue when generating the certificate.

If the certificate generation is successful, then the certificates will be installed, as we ticked staging these won’t be valid in the browser, so click next again and repeat the steps, this time with staging unchecked.

You may need to refresh/restart your browser for the certificate to appear, especially if you have your site open.

And that’s all there is to it, in three months time this will auto renew your SSL certificates.

Check back into Azure and the websites TLS/SSL settings section you should see a TLS/SSL binding.

How To Add Drag ‘n’ Drop Angular Material Table

Image: https://material.angular.io/

Blog Post #007

Duncan Faulkner – February 2020

The Angular Material mat-table component probably has the most examples on the Angular Materials website. But I think we can squeeze one more in.

So here is a Drag ‘n’ Drop example.

I needed the ability to drag ‘n’ drop rows in a mat-table for a project I was working on and I thought this would make a good example of how easy it was to implement.

This example requires all the usual setup of an Angular application, rather than fill this post with all the steps to setup an Angular project. I have written a post here that goes through the steps. I will also create a another separate post on adding Angular Material, but for now here is how to add Angular Material to your Angular Project.

Project set up? Let’s add Angular Materials, from the terminal type:

ng add @angular/material

This will add the Angular Material dependencies to the project, next create a directory called shared and then add a new file called material.module.ts in this directory.

Then add the material imports, for this we just need MatTableModule.

// Other Material imports here
import { MatTableModule } from '@angular/material/table';

const MATERIALMODULES = [MatTableModule];

@NgModule({
     imports: [...MATERIALMODULES],
     declarations: [],
     exports: [...MATERIALMODULES]
});
export class MaterialModule {}

Then import material.module.ts file into the app.module.ts file.

// default modules add when app.module.ts was created
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { HttpClientModule } from "@angular/common/http";

// Add these modules to import
import { MaterialModule } from "./shared/materials.module";
import { DragDropModule } from "@angular/cdk/drag-drop";

const MATERIALMODULE = [MaterialModule];
const IMPORTMODULES = [
  BrowserModule,
  AppRoutingModule,
  MaterialModule,
  BrowserAnimationsModule,
  HttpClientModule,
  DragDropModule
];
const APPCOMPONENT = [AppComponent];

@NgModule({
  declarations: [...APPCOMPONENT],
  imports: [...IMPORTMODULES],
  exports: [...MATERIALMODULE],
  providers: [],
  bootstrap: [...APPCOMPONENT]
})
export class AppModule {}

The MaterialModule is the Angular Material file we just created a few moments ago and the DragDropModule is required to implement the drag and drop from the Angular Material CDK (more on this later).

Follow me on Twitter to get notified about my newest blog posts!🐥

In the app.component.html file remove any html and replace it with the following.

<table mat-table [dataSource]="dataSource" cdkDropList [cdkDropListData]="dataSource"
    (cdkDropListDropped)="drop($event)" class="mat-elevation-z8">

    <ng-container matColumnDef="drag">
        <th mat-header-cell *matHeaderCellDef> Drag </th>
        <td mat-cell *matCellDef="let element">
            <mat-icon cdkDragHandle svgIcon="dragVertical"></mat-icon>
        </td>
    </ng-container>

    <!-- Position Column -->
    <ng-container matColumnDef="position">
        <th mat-header-cell *matHeaderCellDef> No. </th>
        <td mat-cell *matCellDef="let element"> {{element.position}} </td>
    </ng-container>

    <!-- Name Column -->
    <ng-container matColumnDef="name">
        <th mat-header-cell *matHeaderCellDef> Name </th>
        <td mat-cell *matCellDef="let element"> {{element.name}} </td>
    </ng-container>

    <!-- Weight Column -->
    <ng-container matColumnDef="weight">
        <th mat-header-cell *matHeaderCellDef> Weight </th>
        <td mat-cell *matCellDef="let element"> {{element.weight}} </td>
    </ng-container>

    <!-- Symbol Column -->
    <ng-container matColumnDef="symbol">
        <th mat-header-cell *matHeaderCellDef> Symbol </th>
        <td mat-cell *matCellDef="let element"> {{element.symbol}} </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;" cdkDragLockAxis="y" cdkDrag [cdkDragData]="row">
    </tr>
</table>

A mat-table component takes a datasource, but we also need to add cdkDropList, a cdkDropListData and cdkDropListDropped to the table. The cdkDropList is a container that wraps a set of draggable items. The cdkDropListData is the datasource to attach to this container. And the cdkDropListDropped emits an event when a user drops an item into the container.

The last <tr> section in the mat-table, needs the collection of columns this is the array containing the column names, and row refers to current row.

The cdkDragLockAxis restricts the dragged element to either vertical or horizontal, this stops the user dragging the item all over the screen if it doesn’t make sense to, for example if you are only dragging a list of items to sort them then locking the axis to the “y axis” would keep this within the boundary of the list. The cdkDrag refers to the item being dragged and cdkDragData is the data attached to this drag instance.

That’s the HTML section, now for the code section.

import { Component } from "@angular/core";
import { IconService } from "./services/icon.service";
import { CdkDragDrop, moveItemInArray } from "@angular/cdk/drag-drop";

export interface PeriodicElement {
  name: string;
  position: number;
  weight: number;
  symbol: string;
}

const ELEMENT_DATA: PeriodicElement[] = [
  { position: 1, name: "Hydrogen", weight: 1.0079, symbol: "H" },
  { position: 2, name: "Helium", weight: 4.0026, symbol: "He" },
  { position: 3, name: "Lithium", weight: 6.941, symbol: "Li" },
  { position: 4, name: "Beryllium", weight: 9.0122, symbol: "Be" },
  { position: 5, name: "Boron", weight: 10.811, symbol: "B" },
  { position: 6, name: "Carbon", weight: 12.0107, symbol: "C" },
  { position: 7, name: "Nitrogen", weight: 14.0067, symbol: "N" },
  { position: 8, name: "Oxygen", weight: 15.9994, symbol: "O" },
  { position: 9, name: "Fluorine", weight: 18.9984, symbol: "F" },
  { position: 10, name: "Neon", weight: 20.1797, symbol: "Ne" }
];

/**
 * @title Basic use of `<table mat-table>`
 */
@Component({
  selector: "app-root",
  styleUrls: ["app.component.scss"],
  templateUrl: "app.component.html"
})
export class AppComponent {
  constructor(private iconService: IconService) {
    this.iconService.registerIcons();
  }

  displayedColumns: string[] = ["drag", "position", "name", "weight", "symbol"];
  dataSource = ELEMENT_DATA;

  drop(event: CdkDragDrop<PeriodicElement[]>) {
    moveItemInArray(this.dataSource, event.previousIndex, event.currentIndex);
    console.log(event.container.data);
  }
}

The drop event takes a generic CdkDragDrop<> in this case it’s a array of type PeriodicElement, this refers to the row that is being dropped. The moveItemInArray takes a datasource, the previousIndex and the currentIndex. The dataSource is the array (in this example the ELEMENT_DATA), the previousIndex is where the item came (position 10) from and the currentIndex (position 1) is where the item is being dropped. This method does all the hard work of the sorting etc… we just need to write the code to persist this and update the view (I’ve not done that in this example). If you check the console.log at this point then we can see that the data has been reordered.

And that’s all there is to add Drag n Drop to a mat-table.

Tips On How To Write Good Git Commit Messages

Photo by Yancy Min on Unsplash

Blog Post #006

Duncan Faulkner – February 2020

This post is slightly off my usual topics of Angular and Angular Material though its loosely linked to Angular I felt it was worthy of a post as this is something we as developers do all the time.

I’ve been using Git for about five years or so now, and writing Git Commit messages can be hard (I would say this is just as hard as coming up with meaningful function and variable names in code), especially when trying to be consistent with the formatting of the message.

Follow me on Twitter to get notified about my newest blog posts!🐥

I’m sure we’ve all written commit messages like this, the way I have written them have all started like:

git commit -m "name-ticket-number - bug fix" 

git commit -m "name-ticket-number - more work" 

git commit -m "name-ticket-number - minor changes" 

git commit -m "name-ticket-number - work on feature X"

git commit -m "name-ticket-number - updated Y on..."  

At first these don’t appear to look that bad, you get a rough idea of the change. But when faced with hundreds of commit messages (and from different developers), finding a specific Commit in the Git History becomes that much harder.


Up until quite recently I hadn’t realised that you can provide multiple -m flags with a commit to add multiple commit messages.

Why would you want to add multiple messages to a commit?

Surely we can just cram everything into one message?

By adding multiple messages you can provide much more detail and be more specific about the commit, it doesn’t need to be an abbreviated or cryptic message that only you can remember what the commit was for.

Now, when reading a commit message in say GitLab, the first message becomes the Subject, the second message is the Body, the third message is the Footer (I’ll come to this in a bit), their values are concatenated as separate paragraphs.

git commit -m "name-ticket-number - fixed..." -m "body/description..." -m "footer..."

So this is a little better, adds more detail to the commit message, but we could improve this further by following a convention.

Conventional Commit Standard (I didn’t know that was a thing either!). This is a set of guide lines for creating Git Commit messages that follow a particular format.

The format is:

type(scope):subject
BODY
FOOTER

Each commit message consists of a header, body and footer. The header is mandatory, scope is optional, subject is mandatory. When writing a commit message try and limit each line of the commit to a max of a 100 hundred characters, so it can be read easily on GitLab etc…

It is also acceptable to add the project name or a ticket number in square brackets, for example: [foo-1234] fix(user-service): changed user….
If using something like Jira this [foo-1234] becomes a hyperlink to the ticket from Gitlab / Github.

Type

The type must be one of the following:

  • build: Changes that affect the build
  • ci: Changes to our CI configuration files and scripts
  • docs: Documentation only changes
  • feat: A new feature
  • fix: A bug fix
  • perf: A code change that improves performance
  • refactor: A code change that neither fixes a bug nor adds a feature
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
  • test: Adding missing tests or correcting existing tests

Revert

If a commit reverts a previous change then the type should begin with revert: and the body should explain what this change is reverting.

Scope

The scope refers to the section that this commit belongs to Components, Services, Directives, Pipes for example user-component, user-service, user-directive etc.. Other scopes include changelog, styles, test and refactor.

Subject

The subject should contain a short description of the change, and written in present tense, for example “change” and not “changed” or “changes”.

Body

The body should contain a longer description of the change, try not to repeat the subject and keep it in the present tense as above.

Footer

The footer is used to highlight where issues have created a breaking change in your commit. The footer should start with BREAKING CHANGE: the body should contain a description of the breaking change.

The footer can also be used to reference other issues, for example: “Fixes ticket #123”.


Examples

A few examples of Git Commit messages using this new format:

feat(order): add a save button
add the save button to the sales order form

docs(manual): document the order process
create new document explaining the sales order process

fix(user): update the user menu
update to the user menu to fix the broken menu item

fix(user-service): change save method in API
BREAKING CHANGE: change to the save method to include additional parameters


Follow me on Twitter to get notified about my newest blog posts!🐥

Let me know in the comments what you think to this format.

How To Set Up An Angular Project Using The CLI

Blog Post #005

Duncan Faulkner – February 2020

I find myself repeating the Angular setup process at the start of most posts I write. So to save time. I will write it out here and reference this post when referring to setting up an Angular application in the future.

This will be a standard setup, for setting up a project with NX Workspaces see my previous post here.

Step one:

Open a terminal and create a directory to store our application in.

cd development
mkdir myproject 
cd myproject

Step two:

If you have nvm, node and the angular cli installed you can skip this step.

To install node.js, you could install this from nodejs.org, but for greater flexibility, I find installing node version manager (nvm), will save a lot of hassle next time you need update node.

Note: Remember to check the version number and update this in the scripts below, at the time of writing this was v0.35.2, it may be different.
Note: You only need to run one of these.

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash

or

wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash

Once installed you should be able to check the version, in the terminal type:

nvm --version
0.35.2

Now we have nvm installed we can install node. Again from the terminal type:

nvm install #and the version of node you want to install, for example:
nvm install 12.14.1

#Other useful nvm commands are:
nvm ls #this will list all installed versions of node
nvm use 12.6.0 #will use this version of node.

nvm alias default v9.3.0 #this will set the default version.

Now we have node installed we can now install angular cli, from the ternimal type:

npm install -g @angular/cli 

This will install the angular cli globally, as denoted by the -g. After installation is complete check the version of angular by typing:

ng v
angular version printed out in console
screen shot of angular version

Step three

Now that’s all setup, lets create a project, in the terminal type:

ng new my-first-project

The angular cli will ask the following questions.

? Would you like to add Angular routing? (y/N) y

 CSS
>SCSS [https://sass-lang.com....] #select this one
 Sass [https://sass-lang.com...]
 Less [http://lesscss.org...]
 Stylus [http://stylus-lang.com...]

That should start the process off and it will install all the dependencies and create our project. You may see some warnings at the end of the install, these are dependencies they may need to be updated, which we will do at the end. For now switch to the my-first-project directory and open up VS Code.

cd my-first-project
code-insiders .
my-first-project shown in vs code

And that’s all there is to it.

Step four

Update the project to the latest versions. In the terminal type:

ng update #this will list any packages that have an update available.

This will check to see if there are any updates, if there are then run:

ng update --all

#there are a couple of additional flags that can be used (with caution!) these are:

ng update --all --allow-dirty #this allows the update to happen when the current branch (if using version control) has not been checked in

ng update --all --allow-dirty --force #this will force the updates, though chances are compatibility will be broken and you may need to roll some packages back to a previous state. 

There is a good chance that this will fail, mainly because there are newer versions of certain packages that would break compatibility.

The next step would be to add Angular Material to the project. For that I will create a separate post so as to keep this purely for the standard setup.

Thanks for reading enjoy…


Previous Posts

How To Set Up A Mono-Repo And Workspace In Angular

Blog post #003

Duncan Faulkner – January 2020

NX Workspaces

What is an NX workspace and what are the benefits of using one?

This blog post takes a look at what an NX Workspace is, how to set one up and to discuss some of the benefits.

An NX Workspace is a product from nrwl.io that extends the Angulars CLI (Command Line Interface) and creates an application project structure for a variety of frameworks and libraries, not just Angular. The structure is based around a monorepo layout and is a technique that large enterprise organisations are adopting. The idea behind the monorepo is that every project or library are all contained in one large project (separated into individual projects under a parent folder).

Wait…all projects in the same solution? That can’t be best practice?

At first this sounds a little odd, why would you bundle all of your projects into one large repo? Surely that must be harder to find things, what about versioning of projects, how do you build individual projects? Can you build individual projects? Do you have to build all of them, what if they are independent projects? These are some the questions I asked when I first started to use the NX Workspace and I will try and answer these here.

Lets run through setting up an empty NX Workspace and we’ll add an Angular project later on. Open a terminal window and type:

npx create-nx-workspace@latest myproject
cd myproject

This will create a new NX Workspace, after a few seconds it will ask for a type of project to create. At the moment these are:

# sample one
What to create in the new workspace (Use arrow keys)
 empty             [an empty workspace]
 web components    [a workspace with a single app built using web components]
 angular           [a workspace with a single Angular application]
 angular-nest      [a workspace with a full stack application (Angular + Nest)]
 react             [a workspace with a single React application]
 react-express     [a workspace with a full stack application (React + Express)]
 next.js           [a workspace with a single Next.js application]

For now, using the arrow keys select the `empty` project. The next step, is to select which CLI to use, choose NX.

NX          [Extensible CLI for JavaScript and TypeScript applications]
Angular CLI [Extensible CLI for Angular applications (Default).]

Note: The NX Workspace is not a replacement for the Angular CLI, it extends the CLI by way of schematics and builders. This allows you to do more things, add different project types for example, an Angular front end with a Nest JS back end or a Node and Express application or React and Express and also the ability to build your own, but all contained in a monorepo structure.

After a few minutes a new empty project is created. Using your favourite text editor open the project. I’m using VS Code, so in the terminal cd into the myproject folder and type code-insiders . (don’t forget the dot!).

An empty NX workspace in VS Code.

empty nx workspace

What’s in the project?

The .vscode folder contains all the VS Code setting files. The apps folder is where all the projects like Angular, React live. The libs folder is where all the library projects live. And the tools folder is where all the pre/post scripts and schemas live. The rest of the files are just config type files.

The main difference between an NX Workspace and an Angular CLI project is the addition of the apps folder. This is the root folder where all projects (except for libraries) live. It’s also worth noting here that in an NX Workspace project there is only one package.json file for the whole solution.

At the moment what we just created doesn’t do anything, we need to add a application to this empty workspace (Angular/React/Vue for example). In the following example I will be setting up and Angular project.

From the terminal type:

npm install --save-dev @nrwl/angular

This will setup the NX Workspace so we can install the Angular project in this instance, there are other options. Because we setup the Workspace using NX CLI we have to use nx commands rather than ng commands, if you selected Angular CLI or are upgrading an existing Angular project to an NX Workspace then you can use the ng commands.

Next from the terminal type nx g @nrwl/angular:app myapp this will create our Angular project my-app and a myapp-e2e project, it will also set the projects to use Jest and Cypress both of these are testing suites (Jest for unit and Cypress for end to end testing).

nx workspace with angular project

Now we should be able to build and run the project, though at the moment there is just a home page with references to NX and the nrwl.io website. Both projects should build and run (including the myapp-e2e application, though this will need to be started separately).

At this point we can now start to build our application and we are not limited to one type of project either. We could have also created an React app and an Express/Node API application and have them all working together if our project called for it.

If our applications have common components that need to be shared, we can (and should) create a library project for this component. The advantages of using a library project keeps the main application as small as possible and allows us to reuse the same component time and time again across all project types, all we need is to include a reference to the library from our projects.

I should just point out that libraries were introduced as part of Angular (I think in version 6) and not NX Workspaces, the NX workspace creates a library folder by default.

So that’s the basic workspace created and with a simple Angular application. Back to the questions I raised at the beginning:

  • Surely that must be harder to find things?
  • What about versioning of projects?
  • How do you build individual projects?

Surely that must be harder to find things.

At first this might seem to be harder, but in a short space of time it’s quite easy to navigate around, each application is in it’s own folder, and you could create parent folders for both application and the end to end project. Which would make it less cluttered under the apps folder.
Create library projects for code reuse, for components that are common and potentially common across multiple applications, for example login screens, user creation, role and permission assignments, menus these make for excellent library components.

What about versioning of projects.

Because all the applications reside under one repository they all have the same version number. This may not be ideal, for example what if you add a new application to an existing NX Workspace that has had previous production releases then the new application will start at that version?

I guess this does work to some extent, but I need to investigate this further there are still some questions that I feel need further clarification on and I’m collating some of these and I will update this post when I have something to add.

How do you build individual projects?

Is it possible to build individual projects – simple answer is yes you can. Make sure you have the NX CLI installed globally.

npm install -g @nrwl/cli 

Then to build a specific application run:

nx run myapp:build

Your application should build and it should appear under the dist directory. In this directory you will see a *-es2015.js and *-es5.js you need to deploy both sets to your web server, when a client connects using an es2015 compatible browser the *-es2015.js files will be used, likewise if an older browser is used then the *-es5.js are used.

Conclusion

The NX Workspace is a great addition to the Angular CLI, I haven’t used all that it has to offer as there is a lot to it, but what I have used is excellent. It deals with the basic structure of an application and it feels natural when you get used to it’s layout of the project.

Note: If you create a empty NX Workspace and then add an Angular project, you might be wondering why there is no angular.json file, it is there but it’s called workspace.json.


Previous Posts…

How To Use Typescripts …SpreadOperator In Angular

Blog post #004

Duncan Faulkner – February 2020

Recently I’ve found myself looking at ways I can improve the reading of my code. Just little things, just to tweak this or that just to make the code a little more easier on the eye.

Recently I’ve started using the spread operator (…) as I find this looks a lot cleaner and makes code easier to read.

In this post I’m going to show you how to use the spread operator in your Angular module files.

Most of the module files I’ve written have all looked the same. Starting at the top of the file are the Imports, followed by the NgModule with some or all of the following: Imports, Exports, Declarations, Entry Components and Providers.

Depending on the number of imports into the module this can have a lot of repeated code. So these files are ideal for the spread operator, reducing the repeated code. Lets see and example.

import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipsModule } from '@angular/material/chips';

import { NgModule } from '@angular/core';

@NgModule({
  imports: [
    MatAutocompleteModule,
    MatButtonModule,
    MatButtonToggleModule,
    MatCardModule,
    MatCheckboxModule,
    MatChipsModule
  ],

  declarations: [],
  exports: [
    MatAutocompleteModule,
    MatButtonModule,
    MatButtonToggleModule,
    MatCardModule,
    MatCheckboxModule,
    MatChipsModule
  ],
  entryComponents: [],
  providers: [MatIconRegistry]
})
export class MaterialModule { }

The above example is from the Mat-Icon material module file and shows the same things repeated three times in this example. Lets refactor this code.

import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipsModule } from '@angular/material/chips';

import { NgModule } from '@angular/core';

const MODULES = [
  MatAutocompleteModule,
  MatButtonModule,
  MatButtonToggleModule,
  MatCardModule,
  MatCheckboxModule,
  MatChipsModule
];
@NgModule({
  imports: [
    ...MODULES
  ],
  declarations: [],
  exports: [
   ...MODULES
  ],
  entryComponents: [],
  providers: [MatIconRegistry]
})
export class MaterialModule { }

Now, unfortunately the Angular Material imports need to be individually imported for each feature separately, but it does reduce the code (a bit) and it does help with having just one copy to manage, lets have another example, that I have changed to use the spread operator (you should be able to visualise what this looked like before).

import { NgModule } from '@angular/core';
import { VendorModule } from './vendor.module';

import {
  AnalysisComponent,
  ChartComponent,
  IconComponent,
  PodComponent,
  SpinnerComponent,
  TermsComponent,
  ChangePasswordComponent
} from './components';

import {
  PortPipe,
  NumericPipe,
  AsteriskPipe,
  SplitIpPipe,
  RemoveUnderscorePipe,
  AddUnderscorePipe,
  FormatBytesPipe
} from './pipes';

import { NumberDirective } from './directives';

const COMPONENTS = [
  AnalysisComponent,
  ChartComponent,
  IconComponent,
  PodComponent,
  SpinnerComponent,
  TermsComponent,
  ChangePasswordComponent
];

const PIPES = [
  PortPipe,
  NumericPipe,
  AsteriskPipe,
  SplitIpPipe,
  RemoveUnderscorePipe,
  AddUnderscorePipe,
  FormatBytesPipe
];

const DIRECTIVES = [NumberDirective];

const MODULES = [VendorModule];

const ENTRYCOMPONENTS = [TermsComponent, ChangePasswordComponent];

@NgModule({
  imports: [...MODULES],
  declarations: [...COMPONENTS, ...PIPES, ...DIRECTIVES],
  exports: [...COMPONENTS, ...PIPES, ...DIRECTIVES, ...MODULES],
  entryComponents: [...ENTRYCOMPONENTS],
  providers: [...PIPES]
})
export class SharedModule {}

In the example above, imports for the component, pipes and directives are all grouped together. Constants are configured, and used in the NgModule section of the file . The important part of this file is the NgModule section and you can easily now read that section without all the clutter of all the various components, pipes and directives all over the place.

The spread operator include options for object destructing, rest params and array destructing (as shown above).

Thanks for reading!


Previous Posts…

How To Set Up Angular Materials Mat-Icon Component

Part Two

Blog Post #002

Duncan Faulkner – January 2020

I hadn’t intended to write about this just yet, but I got a chance to investigate this sooner than I had thought. I was refactoring part of a project that was using the Mat-Icon component, with an icon.module.ts file as I’d discussed in the previous post.

I wanted to see if I could improve on the code and reduce the amount of repeated code by iterating over a file and registering the icons.

I started with a JSON file and populated it with some image names and added the HttpClientModule to use an http.get() method to read the JSON file. All good so far, the file is reading and looping with image names being returned and passed into the matIconRegistery.addSvgIcon() method.

I thought I might get some issues, but it all seemed to work fine, except the application was erroring, the issue was that the application was loading before the call to the JSON file had returned with the results, so even though I was getting images and registering icons, the app had already tried to use the icons and they weren’t there at that point, and the images were being returned after the app had started.

OK, so may be this is a timing issue and it doesn’t work in the icon.module.ts file, so I moved code to the app.component.ts file. Nope, the results were the same. This was not the result I was expecting and it was a little odd because the icon.module.ts file was loading images before.

I’m going to revisit this at a later date, as I would like to understand what the issue is and why it was behaving the way it did, my guess is it has something to do with the http.get() request and the timing around that.

But as I needed to get this refactoring completed I needed to look for another solution.

After a bit of research I found an answer and that was to create a service to handle the iterating and registering of icons. Instead of a JSON file to store a list of images the solution was an enum, with a key value structure.

export enum Icons {
    add = 'add',
    power = 'power',
    save = 'save'
}

To keep things simple I’ve made the key and the image name the same.

The service is quite simple:

// imports
import { Icons } from '../image.enum';

@Injectable({ providedIn: 'root'})
export class IconService {
  constructor(
     private matIconRegistry: MatIconRegistry,
     private sanitizer: Domsanitizer
){}

registerIcons(): void {
   this.load(Object.values(Icons), 'assets/images/icons');
}

private load(key: string[], url: string): void {
   key.forEach(icon => {
     this.matIconRegistry.addSvgIcon(
       icon, 
       this.sanitizer.bypassSecurityTrustResourceUrl(`${url}/${icon}.svg`)
   ); 
 });
 }
}

And on the app.component.ts file call the registerIcons() function.

// imports
import { IconService } from './icon.service';

export class AppComponent implements OnInit {
   constructor(private iconService: IconService) {
      this.iconService.registerIcons();
   }

}

And that’s it, not what I had in mind when I started this, but it does deliver what I wanted to do, and that was to reduce the amount of code to register icons. Though I guess the enum could get pretty lengthy if there are a lot of images.

I have to thank Stefanos Kouroupis -dev.to – for his article and code on this as I wouldn’t have thought about using a service for this and this has changed my thinking. I wanted to include here it as a follow up to my previous post for completeness, you can see his full article here https://dev.to/elasticrash/using-custom-made-svg-icons-through-the-angular-material-library-2pif.

I will add a github repo of the code in a day or two.


Previous posts…

How To Set Up Angular Materials Mat-Icon Component

Using local SVG images.

Blog post #001

Duncan Faulkner – October 2019

When developing an Angular application with Angular Material, there comes a point when we need to add icons on our components, or buttons etc… Angular Material has the Mat-Icon component for doing this. This component works with web fonts like Font-Awesome for instance, simply by adding the name of the image required and an image is displayed.

For example: <mat-icon>home</mat-icon>

Note: Requires Angular application and Angular Material is installed/configured and a reference to a web font like Font-Awesome library is all set up

But what about if you have custom icons that are not part of a web font? And would like to makes changes to the icon, for example: change the colour on hover or on a specific condition at run time?

In a recent project I had a bespoke set of SVG icons, the Angular web application was to be installed on a server that didn’t have access to the internet, so the images had to be local. I wanted to use the Mat-Icon component out of the box (in an early version of the project I had a custom icon component). I still wanted to be able to change the colours of the icons at various stages throughout the application based on certain conditions as well as on hover. This post covers how I achieved that.

SVG Icons

There are a number of different ways to register an icon with the Mat-Icon component, this post discusses addSvgIcon, the others are addSvgIconInNamespace, addSvgIconLiteral or  addSvgIconLiteralInNamespace and are all methods of  MatIconRegistry and I might cover these in more detail in a future post.

Setting up an Angular project

Note In this post, I’m not going to step through the creation of an Angular application as there are so many already online. Plus I tend to create Nx workspaces for all my Angular projects as I prefer this project layout. I plan to do a blog post on this very soon, for the moment though see Getting started with Narwhal’s Nx Workspaces.

In the newly created Angular project, create a shared directory and add a new file named material.module.ts. I like to separate Angular Material imports into their own module, I also create a separate module for other 3rd party components, just makes it easier to import later, especially using the NX workspace layout and feature folders.

// Material Module example.
 // All other Angular Material component imports here
 // but the important ones are…
 import { MatIconModule, MatIconRegistry } from "@angular/material/icon";
 @NgModule({
   declarations: [],
   imports: [
     // Other material imports removed for brevity,
     MatIconModule
   ],
   exports: [
     // Other material exports removed for brevity,
     MatIconModule,
     MatIconRegistry
   ],
   entryComponents: [],
   providers: [MatIconRegistry]
 })
 export class MaterialModule {}

MatIconModule is the module for the component, and the MatIconRegistry is a service to register and display icons. Add a reference to the material.module.ts in the app.module.ts. Don’t forget to export it as well, otherwise it won’t be available and Angular will not know anything about the Angular Material components.

// Include the material module in app.module.ts
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { AppComponent } from "./app.component";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { MaterialModule } from "./shared/material.module";

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, BrowserAnimationsModule, MaterialModule],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

Now we have the Angular Material components set up and configured we need to register the icons, before we can use them. For the moment we are just going to add these to the app.component.ts to get up and running, we’ll look at a better method later on.

// First Example
@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.scss"]
})
export class AppComponent {
  constructor(private matIconRegistry: MatIconRegistery) {
    this.matIconRegistry.addSvgIcon("home", "assets/img/icon/home.svg");
    this.matIconRegistry.addSvgIcon("add", "assets/img/icon/add.svg");
  }
  // or we could do this, and chain the addsvgIcon methods.
  // we'll use this method going forward in this post
  // {
  //  this.matIconRegistry.addSvgIcon('home','assets/img/icon/home.svg')
  //  .addSvgIcon('add','assets/img/icon/add.svg');
  // }
}

Add this to the app.component.html page, we’ll discuss this in more detail in a bit.

<!-- First HTML example -->
<div>
     <mat-icon svgIcon="home"></mat-icon>
     <mat-icon svgIcon="add"><mat-icon>
</div>

At this point we are not going to see much in the browser as we have an issue with the image URL. If you open the browsers console section, you will see the following error:

Error: unsafe value used in a resource URL context.

So what does this error mean? A brief explanation from the Angular Material website says:

To prevent an Cross Site Scripting (XSS), SVG URL’s and HTML strings passed to MatIconRegistry must be marked as trusted by the Angular’s DomSanitizer service. Icons are fetched via XmlHttpRequest and must have their URL’s on the same domain as the containing page or configured to allow cross-domain access.

So lets add the DomSanitizer and fix this issue.

// Second Example - with the DomSanitizer
@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.scss']
})
export class AppComponent {
 constructor (
 private domSanitizer: DomSanitizer,
 private matIconRegistry: MatIconRegistery
 ){
 this.matIconRegistry
 .addSvgIcon('home',this.domSanitizer.bypassSecurityTrustResourceUrl('assets/img/icon/home.svg')
 .addSvgIcon('add',this.domSanitizer.bypassSecurityTrustResourceUrl('assets/img/icon/add.svg')
 // add other icons here....;
 }
}

The call to bypassSecurityTrustResourceUrl takes a URL as a parameter and sanitises it so that an attacker cannot inject a ‘JavaScript:` URL for example, see official documentation on DomSanitizer. Now that we have this in place we should now see two icons in the browser.

If we have a lot of icons to add this means lots of typing and lots of repetitive code, so lets refactor this some more. Start by removing all of this code (including the constructor) from the app.component.ts. This shouldn’t really be in the app.component.ts file. Lets create another new module in the shared directory and call it icon.module.ts and then add the following:

// Third Example - icon module
import { NgModule } from "@angular/core";
import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";
import { MaterialModule } from "../shared/material.module";
import { MatIconRegistry } from "@angular/material/icon";
@NgModule({
  declarations: [],
  imports: [MaterialModule],
  exports: [],
  providers: []
})
export class IconModule {
  private path: string = "../../assets/images"; // change this
  constructor(
    private domSanitizer: DomSanitizer,
    public matIconRegistry: MatIconRegistry
  ) {
    this.matIconRegistry
      .addSvgIcon("home", this.setPath(`${this.path}/home.svg`))
      .addSvgIcon("add", this.setPath(`${this.path}/file-plus.svg`));
  }
  private setPath(url: string): SafeResourceUrl {
    return this.domSanitizer.bypassSecurityTrustResourceUrl(url);
  }
}

Overall, that’s not too bad. We are only writing out the domSanitizer code once in the private method but more importantly, all the code is out of the app.component.ts file and is now a self contained module. If there are a lot of icons to add then this file will get a bit long, but the typing has gotten shorter (well, a little shorter at least). You could change the constructor to iterate through a .json file of image names, the path wouldn’t change and could be a const, which would only mean maintaining a .json file with any new images. I might look at doing that in a follow up post.

Note: Don’t forget to add this new icon.module.ts to the app.module.ts otherwise it won’t work.

Using the Mat-Icon component

So how do we use the mat-icon component? As seen earlier in this post we add the following code to our app.components.html page.

<!--First HTML example-->
<div>
    <mat-icon svgIcon="home"></mat-icon>
    <mat-icon svgIcon="add"></mat-icon>
</div>

This is a very simple example is showing how to put a home and an add icon on a page/component. This is not too dissimilar to how we would use this component with web fonts, but we are now using the svgIcon input property. The value we give to this input is the first parameter used in our call to register the .addSvgIcon(‘home’, …) in this case home.

Now we have an icon in place, how do we change the colour of the icon when someone hovers over it for example?

Change the icon colour

A Home icon. Example icon copy this into a file with the SVG extension.

<svg version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z" fill="#00FFFF"></svg>

In the above XML, I’ve removed all the namespaces etc… The main part here is the fill=”#00FFFF”. This sets the colour for the image, in this case to AquaMarine.

If the fill=”#00…” property is not there and you want a different colour to the default black, then you can add it to the path as above. It’s optional.

I usually add the fill property and set it to white and then change it, in SCSS as and when required (because this example had a white background I used another colour).

!<-- Second HTML example -->
<div>
  !<-- other code omitted for brevity -->
  <a mat-button>
      <mat-icon svgIcon="home" class="btn-icon"></mat-icon>
  </a>
</div>

Add a class to the mat-icon html tag, as above. Next, add the SCSS for the class so that, when a user hovers over the button the colour of the icon changes.

.btn-icon {
  &:hover {
    path {
      fill: rgba(#00ffff, 1);
    }
  }
}

Note: One thing I did forget to add to the app.component.ts above, was the following line: encapsulation: ViewEncapsulation.None, without this then the hover affect doesn’t work.

@Component({
  selector: "mat-icon-demo-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.scss"],
  encapsulation: ViewEncapsulation.None
})
export class AppComponent {
  title = "mat-icon-demo";
}

Enjoy!

There is a github repo for this blog post at: https://github.com/Anglebrackets-io/mat-icon-demo


Previous posts…