Creating PWA with Angular 5. Part 1: Getting started with framework, creating an application, hosting it on github-pages.

I’ve been delaying my acquaintance with new Angular framework for too long time. As a big fan of AngularJS v1.x, I didn’t get the necessity of so radical changes came with new versions of it.

My eyes were opened during GDG DevFest Ukraine 2017. Tired and hungry by the end of the day 1, I’ve charged my final reserve force on the speech named “Web goes Native: Progressive Web Apps with Angular” by Shmuela Jacobs. And that was it! Inspiring, productive, fast and easy. I couldn’t believe that ‘progressifying’ of the web application can be just that simple.

A month later, when the new Angular 5 was announced and released, I decided to deal with it at all costs and create my first progressive web application. My experiment was successful, and now I am glad to share with you my steps and results.

Step 1. Getting started with Angular.io

As the most of us — I am a lazy person, so really felt in love with the such powerful helper tool — Angular CLI. It’s a magic wand of your project, so don’t be scared of it. The installation is as easy, as every npm module. (My current npm version is 5.5.1, node is 9.2.0).

npm install -g @angular/cli

❗️Check your cli version. 1.5.x is not working properly in some cases, so make sure you are using the 1.6 or higher. My current is 1.6.0-rc.0.

Time to generate a new project. Choose a nice, recognizable name for it. Following my instructions, you are going to create an app, that generates a new cute photo of a cat every time you visit it. Let it be PWCat (abbr. for Progressive Web Cat 😺 )

ng new PWCat

It can take a couple of minutes to set up a new project — nice chance to make a cup off coffee ☕️. Cheers!

Good news. We do not need any local web server for deployment. Just run in your terminal:

cd PWCat
ng serve --open

Last command will open a new tab in your browser (make sure that it’s Chrome!) on the http://localhost:4200/, serving your application with some default content. If everything is ok, you will see:

Open PWCat project in your favorite IDE. I’ve chosen the Visual Studio Code, ‘cause it supports the Typescripts without any additional installable packages. Take some time enjoying how many code you have already written 😄. You can read about all that generated files here.

To change the visible name of our application, let’s go to the app.component.ts file and change it’s title property:

// src/app/app.component.ts
...
export class AppComponent {
title = 'Progressive Web Cat';
}

Step 2. Adding Angular Material framework

We don’t want to be distracted by any styling, so material.angular.io is our salvation. Let’s install it through the terminal:

npm install --save @angular/material @angular/cdk

Choose one of pre-built themes for our app. Import it in styles.css file

/* src/styles.css */
@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';

Also we can add normalize.css module to prettify default html styling. In terminal:

npm install --save normalize.css

In styles.css:

/* src/styles.css */
@import '~normalize.css/normalize.css';
@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';

Now we can add material designed modules to our app. To begin with, let’s import the tool bar component. Open the app.module.ts file. You need to add only two lines of code:

// src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { MatToolbarModule } from '@angular/material';
import { AppComponent } from './app.component';
@NgModule({
declarations: [ AppComponent ],
imports: [
BrowserModule,
MatToolbarModule
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule { }

❗️Don’t forget to include MatToolBarModule to ngModule decorator.

Now we can use toolbar in any place of our app. Go to the app.component.html, and remove all generated content with following lines:

<!-- src/app/app.component.html -->
<mat-toolbar color="primary">
{{ title }}
</mat-toolbar>

By setting the color of element to be ‘primary’ we make it use the main theme color. If everything was done correctly, our app will show us a pretty enough header:

Step 3. Our first Angular component

We need to add and image card element to our app, where cute cats will be generated. Let it be the separate component, and nothing can be so easy, as generating components in Angular 5:

ng generate component img-card

As a result you can see that angular has generated a new folder for you with all necessary files. Also take a look at app.module.ts. New component has been imported automatically.

// src/app/app.module.ts
...
import { AppComponent } from './app.component';
import { ImgCardComponent } from './img-card/img-card.component';
@NgModule({
declarations: [
AppComponent,
ImgCardComponent
],
  ...

To use new component in application just put it’s selector in the necessary place.

<!-- src/app/app.component.html -->
<mat-toolbar color="primary">
{{title}}
</mat-toolbar>
<app-img-card></app-img-card>

And again let’s use material outlook for our card.

// src/app/app.module.ts
...
import { 
MatToolbarModule,
MatCardModule
} from '@angular/material';
...
@NgModule({
...
imports: [
BrowserModule,
MatToolbarModule,
MatCardModule
],
...
})

Due to angular material docs, card template should look like:

// src/app/img-card/img-card.component.html
<mat-card>
<img
src="https://cataas.com/cat/says/Progressive%20Web%20Cat"
alt="Cute cat"
mat-card-image>
</mat-card>

As a source for cat images we will use awesome free service called cataas.com, which api is quite straightforward.

Also, it would be nice to have some ‘Refresh’ button to get new cat pictures if we want to. Try to add MatButtonModule by yourself.

Time to write some code

Let’s make the image source dynamic. Go to the img-card.component.ts and create a new class called something like CatImage.

// src/app/img-card/img-card.component.ts
...
class CatImage {
message: string;
api: string;
fontsize: number;
}
...

Now we can add a private property image of type CatImage for ImgCardComponent. The property src should be public and of type string. Take a look on the way we use that properties in the ngOnInit method:

// src/app/img-card/img-card.component.ts
...
export class ImgCardComponent implements OnInit {

private image: CatImage = {
message: 'Progressive Web Cat',
api: 'https://cataas.com/cat/says/',
fontsize: 40
};
  public src: string;
  ngOnInit() {
this.src = this.image.api + this.image.message;
}
...

To make our button alive we should create a new function (public method), that will make some magic for us. I called mine generateSrc:

ngOnInit() {
this.generateSrc();
}
generateSrc(): void {
this.src = this.image.api + this.image.message +
'?size=' + this.image.fontsize;
}

Keyword void shows that our function has nothing to return.
Size is one of the image options provided by cataas.com api.

Time to make some changes in img-card markup. Replace long hard-coded source url with component property {{ src }}. To add event listener to the card button, use (click) attribute.

// src/app/img-card/img-card.component.html
<mat-card>
<mat-card-actions>
<button
color="primary"
(click)="generateSrc()" // parentheses are important!
mat-button
mat-raised-button>
Give me another cat
</button>
</mat-card-actions>
<img
src="{{ src }}"
alt="Cute cat"
mat-card-image>
</mat-card>

Unfortunately, the image is not updating when we click on our Generate button, because actually the value of src stays unchanged. Let’s fix that by adding a fake query param with a timestamp in it.

// src/app/img-card/img-card.component.ts
...
public generateSrc(): void {
this.src = this.image.api + this.image.message +
'?size=' + this.image.fontsize +
'&ts=' + Date.now();
}

After adding some custom styles…

// src/app/img-card/img-card.component.css
.mat-card {
width: 400px;
margin: 2rem auto;
}
.mat-card .mat-card-actions {
padding-top: 0;
}
.mat-card .mat-button {
margin: 0 auto;
display: block;
}

… we will finally get our working app. Enjoy your cats!

Step 4. Hosting our app on GitHub

4.1 Creating a new repository

Create a new repository on github.com (without any init files — to avoid conflicts in the future). Give it a name of your project and press a big green button below.

Go to the terminal and run the following:

git add .
git commit -m "Upload project to github"
git remote add origin git@github.com:{username}/{repo name}.git
git push --set-upstream origin master

And that’s it! Reload repository page to see your code on github.

4.2 Build a production version of application

We don’t need ‘ng serve’ anymore, so turn it off. Time for some production options:

ng build --prod --base-href "/PWCat/"

As a result, we can see a new ‘/dist’ folder with our bundled app.

4.3 GH-pages

If your are new to github pages, your can get familiar with it on the official page. After you’ve got the main idea, let set up gh hosting for our app.

To begin with, let’s create a new branch for it. In terminal:

git checkout -b "gh-pages"
git push --set-upstream origin gh-pages
git checkout master

Go the the Settings tab of your repo, scroll down a little bit and find a ‘GitHub Pages’ section. Make sure that gh-pages branch is set as a source.

Although our site is finally published and available on the provided link, unfortunately we see only a content of README.md file. The problem is that github doesn’t see the index.html file in our root folder, and doesn’t understand that it needs a ‘/dist’ one.

To fix that, we can push to the gh-pages branch only a content from the ‘dist’ folder. To help us, saint people created an angular-cli-ghpages module for that.

npm i -g angular-cli-ghpage
ngh

If everything was done correctly, you will see the ‘Successfully published!’ message. Refresh gh-url of your app and enjoy that moment of glory!

You can find source code of my application here. Live app is available here.

See you in the Part 2.