Frontend Web Development with Angular

Chester Supelana

January 20, 2021

What is Angular?

Angular is a Typescript-based frontend framework created and maintained by the Angular Team at Google, and it is used to create efficient and sophisticated single-app page apps. This is not to be confused with AngularJS which is a different framework (Angular refers to version 2+ of AngularJS after it was completely rewritten).

<br/>

Why use Angular?

  • Organized structure and Separation of Concerns
  • Many useful built-in features (HTTP, routing, animations, etc.)
  • Uses Model-View-Controller pattern
  • Uses Typescript
  • Powerful and easy to use CLI

<br/>

Angular Basics

When designing web apps with Angular, there are a few points/terms to remember:

  • Components - Angular apps are built around components. These are the building blocks
  • Services - Used to share functionality between components. Makes it easy for developers to create loosely-coupled components

<br/>

Some Expectations

In this tutorial, we will be learning how fast and easy it is to develop a simple blog application using Angular. We will discuss components and services, as well as the HttpClient service and routing.

As noted from before, Angular applications are built around components. So when designing apps using Angular, it is important to look at each part of the UI as a component. For example, the image below describes the app that we will be making as well as how it is divided into components. <br/> <div> <img class="img-fluid" src="https://strapi-saperium.s3.ap-southeast-1.amazonaws.com/angular_ws_component_map_5f5f799b62.png" width="100%"> </div> <br/>

Setup

1. Download and install Node.js. Make sure node is installed by checking the version

$ node -v

<br/>

2. Install @angular/cli

$ npm install -g @angular/cli

<br/>

3. Generate a new Angular application. Make sure to use routing and use whatever CSS style you are comfortable with.

$ ng new [app-name]

<br/>

4. Move to the new folder and start the server

$ cd [app-name]
$ ng serve

<br/>

5. Other commands that you can use

  • ng serve: Builds and serves your app, rebuilding on file changes.
  • ng build: Compiles an Angular app into an output directory named dist/ at the given output path. Must be executed from within a workspace directory.
  • Full list here: https://angular.io/cli

6. We can now access the app by going to http://localhost:4200

<br/>

A Quick Look in an Angular app

Before writing any code, let us take a look at some of the files that were automatically created for us.

<br/>

index.html

This file is the main HTML file that is rendered by Angular. It contains the head and body tags, as well as the root component of your entire application.

src/index.html

&lt;!doctype html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
  &lt;meta charset="utf-8"&gt;
  &lt;title&gt;AngularWorkshop&lt;/title&gt;
  &lt;base href="/"&gt;
  &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
  &lt;link rel="icon" type="image/x-icon" href="favicon.ico"&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;app-root&gt;&lt;/app-root&gt;
&lt;/body&gt;
&lt;/html&gt;

<br/>

styles.css

This file contains styles for the entire application. Use this if you wish to implement global styles. For now, let us add these styles for our app and we will see them in action later.

src/styles.css

/* You can add global styles to this file, and also import other style files */
 
body {
    margin: 0;
    font-family: Arial, Helvetica, sans-serif;
}
 
a {
    text-decoration: none;
    font-weight: bold;
}
 
.sub-date {
    font-size: 16px;
    color: #000000a4
}

<br/>

app component

By default, Angular will automatically generate 4 files when creating a component, .ts, .html, .css, and .spec.ts files.

1. app.component.ts: This file contains the component’s declaration and functionality. It houses metadata for the component (specified under the @Component decorator) and all of its properties and methods.

src/app/app.component.ts

import { Component } from '@angular/core';
 
@Component({
  selector: 'app-root', // css selector for this component
  templateUrl: './app.component.html', // path to the template
  styleUrls: ['./app.component.css'] // path to the styles
})
export class AppComponent {
  title = 'angular-workshop';
}

2. app.component.html - This file contains the path to the template (or HTML) of the component.

3. app.component.css - This file contains the path to the styling (or CSS) of the component. Styles under this file will not affect the styles of other components.

4. app.component.spec.ts - This file contains tests for the component to make sure it is working as expected. See Intro to Testing for more information.

<br/>

Templating in Angular

Angular provides a simple but powerful templating functionality. It uses regular HTML syntax but also allows for string interpolation and other cool features.

<br/>

String Interpolation

String interpolation allows developers to run Javascript/Typescript expressions in HTML on the fly. We can use string interpolation by enclosing the expression inside 2 curly braces {{ }}. For example, let’s add a new property to our AppComponent called name.

src/app/app.component.ts (snippet)

export class AppComponent {
  title = 'angular-workshop';
  name: string = 'John';
}

<br/>

For now, we can delete everything in app.component.html and replace it with the following:

src/app/app.component.html

&lt;div&gt;
    Hello, {{ name }}!
&lt;/div&gt;

<br/>

If we check back on our app, the name property of the component was automatically evaluated into ‘John’. <div> <img class="img-fluid" src="https://strapi-saperium.s3.ap-southeast-1.amazonaws.com/angular_workshop_1_678444a8a4.png" width="50%"> </div>

<br/>

Pipes

Angular also provides a functionality to easily transform strings on the fly without needing to declare a method in the component. We can do this by using pipes. In this example, we can transform the string ‘John’ to uppercase using the uppercase pipe provided by Angular.

src/app/app.component.html

&lt;div&gt;
    Hello, {{ name | uppercase }}!
&lt;/div&gt;

<br/>

Now if we check our app, we will see that the text has become uppercase.

<div> <img class="img-fluid" src="https://strapi-saperium.s3.ap-southeast-1.amazonaws.com/angular_workshop_2_e9914b663a.png" width="50%"> </div>

There are other pipes that you can use that are also provided by Angular by default (e.g. DatePipe, NumberPipe, etc.). You can also make your own custom pipe! See more here: Transforming Data Using Pipes.

<br/>

Looping with ngFor

In the case when we want to iterate through an array in the template, we can do that by using the ngFor directive. For example:

&lt;div *ngFor="let number of [1,2,3,4]"&gt;
    # {{ number }}
&lt;/div&gt;

<br/>

This will give us:

<div> <img class="img-fluid" src="https://strapi-saperium.s3.ap-southeast-1.amazonaws.com/angular_workshop_3_de515f9fd9.png" width="50%"> </div>

<br/>

Conditionals with ngIf

We can also conditionally render elements in the template with the ngIf directive. Simply include the ngIf directive in an HTML element and provide a condition. For example:

&lt;div *ngIf="true"&gt;
    This will show on the app
&lt;/div&gt;
 
&lt;div *ngIf="false"&gt;
    This will NOT show on the app
&lt;/div&gt;

<br/>

And this is what it will look like on your app.

<div> <img class="img-fluid" src="https://strapi-saperium.s3.ap-southeast-1.amazonaws.com/angular_workshop_4_9dcc9e5945.png" width="50%"> </div>

<br/>

Styling in Angular

You can also include styles by editing the .css of your component. CSS styles specified for a component will not affect the rest of the application. This is done automatically by Angular via View Encapsulation and for more details about this topic you may read up here: View encapsulation and Using shadow DOM - Web Components | MDN.

<br/>

Creating your first component

Now that we have seen templating and styling in Angular, we can now create our own custom component. To automatically create a new component, we can use the generate command provided for us by the Angular CLI. In the terminal, running this command will generate all the files we need for our new component:

$ ng generate component [path-to-component]/[component-name]
$ ng g c [path-to-component]/[component-name] # short ver.

<br/>

We will now create a simple Header component. We will also create a new folder called components where we will compile all of the components of the app.

$ ng generate component components/header

<br/>

Let us take a quick look at the generated files. In header.component.ts, we will see everything that we were able to see in app.component.ts but with additional methods, a constructor and ngOnInit. The ngOnInit method is a called lifecycle hook and there are many other methods like this one. Lifecycle hooks are special methods that are automatically invoked during a component’s lifecycle. For the case of ngOnInit, this method is called when the component is initialized/created. You can find the details and a complete list of lifecycle hooks here: Hooking into the component lifecycle.

To use this component in our app, first we need to include the selector of the component to the template of AppComponent.

src/app/app.component.html

&lt;app-header&gt;&lt;/app-header&gt;

<br/>

Now, let us fix the template and styles to make it actually look like a header for our app.

src/app/components/header/header.component.html

&lt;div id="header"&gt;
    &lt;h1&gt;blogg.io&lt;/h1&gt;
&lt;/div&gt;

<br/>

src/app/components/header/header.component.css

#header {
    margin-bottom: 30px;
    padding: 20px;
    background-color: #353230;
}
 
h1 {
    margin-top: 0;
    margin-bottom: 0;
    color: white;
}

<br/>

Now if we go to our app, we should see our header rendered on the screen.

<br/>

Creating a model for Blog

Before we create a component to list the blog posts, we will create an interface for Blog. Create a folder src/app/models and create a file called Blog.ts inside the new folder. This is where we will define the Blog interface.

A Blog should have an id (number), title (string) body (string), date (number) and an optional imageUrl (string). We will also need to add the export keyword so that we can import this declaration in other files.

src/app/models/Blog.ts

export interface Blog {
    id: number;
    title: string;
    body: string;
    date: number;
    imageUrl?: string;
}

<br/>

Creating a new listing component

Next we need a component to list our blogs. We will generate a new component called Blog List.

$ ng generate component components/blog-list

<br/>

Again, to use this component in our app, we need to include this to the template of AppComponent, below our header.

src/app/app.component.html

&lt;app-header&gt;&lt;/app-header&gt;
&lt;app-blog-list&gt;&lt;/app-blog-list&gt;

<br/>

Now, we should see that our BlogListComponent is rendered on the screen. But we still want to render a list of blogs. In our component.ts file, we will create a new property called blogs which is an array that contains all the blogs.

src/app/components/blog-list/blog-list.component.ts (snippet)

import { Blog } from '../../models/Blog';
...
export class BlogListComponent implements OnInit {
  blogs: Blog[] = [
    {
      id: 1,
      title: 'Blog Post 1',
      body: 'This is my first blog post',
      date: 1610953242000,
      imageUrl: 'https://static.demilked.com/wp-content/uploads/2018/03/5aaa1cc1d8480-funny-weird-wtf-stock-photos-1-5a39255d726bb__700.jpg'
    },
    {
      id: 2,
      title: 'Blog Post 2',
      body: 'This is my second blog post',
      date: 1610953242000
    },
    {
      id: 3,
      title: 'Blog Post 3',
      body: 'This is my third blog post',
      date: 1610953242000,
      imageUrl: 'https://static.demilked.com/wp-content/uploads/2018/03/5aaa1cce4180b-funny-weird-wtf-stock-photos-57-5a3bb7ba3c266__700.jpg'
    },
    {
      id: 4,
      title: 'Blog Post 4',
      body: 'This is my last blog post... for now!',
      date: 1610953242000
    },
  ]

<br/>

Now, we can render this list on our app by using this list on the HTML template.

src/app/components/blog-list/blog-list.component.html

&lt;div class="list"&gt;
    &lt;div class="card" *ngFor="let blog of blogs"&gt;
        &lt;div&gt;
            &lt;img *ngIf="blog.imageUrl" src="{{blog.imageUrl}}"/&gt;
            &lt;!-- if no image, put a default --&gt;
            &lt;img *ngIf="!blog.imageUrl" src="https://aliceasmartialarts.com/wp-content/uploads/2017/04/default-image.jpg"&gt;
        &lt;/div&gt;
        &lt;h2&gt;{{ blog.title | uppercase }}
            &lt;div class="sub-date"&gt;
                {{ blog.date | date:'mediumDate' }}
            &lt;/div&gt;
        &lt;/h2&gt;
        &lt;p&gt;
            {{ blog.body }}
        &lt;/p&gt;
    &lt;/div&gt;
&lt;/div&gt;

<br/>

And we can add some CSS styles to make our list look better.

src/app/components/blog-list/blog-list.component.css

.list {
    display: flex;
    flex-wrap: wrap;
    width: 85%;
    margin: 0 auto;
    justify-content: center;
}
 
.card {
    border-radius: 10px 10px 0 0;
    padding: 20px 10px;
    margin: 10px;
    flex: 35%;
    max-width: 35%;
    border-bottom: 2px solid rgb(218, 218, 218);
}
 
img {
    width: 100%;
    height: 200px;
    object-fit: cover;
    border-radius: 10px;
    border: 1px solid #000;
}
 
p {
    overflow: hidden;
    text-overflow: ellipsis;
    height: 36px;
}

<br/>

Now if we open our app, it should look like this.

<div> <img class="img-fluid" src="https://strapi-saperium.s3.ap-southeast-1.amazonaws.com/angular_workshop_list_1_2588873324.png" width="100%"> </div>

<br/>

Creating a child item component

Our app is looking quite good but it will be hard to manage a list of blogs using just a single component. So we will define another component to handle each blog in the list (handling just 1 blog sounds a lot easier than handling a list of blogs at the same time). Let us generate a new Blog Item component.

$ ng generate component components/blog-item

<br/>

And we will move some of the template and styles from the BlogListComponent to our new BlogItemComponent.

src/app/components/blog-item/blog-item.component.html

&lt;div *ngIf="blog"&gt;
    &lt;div&gt;
        &lt;img *ngIf="blog.imageUrl" src="{{blog.imageUrl}}"/&gt;
        &lt;!-- if no image, put a default --&gt;
        &lt;img *ngIf="!blog.imageUrl" src="https://aliceasmartialarts.com/wp-content/uploads/2017/04/default-image.jpg"&gt;
    &lt;/div&gt;
    &lt;h2&gt;{{ blog.title | uppercase }}
        &lt;div class="sub-date"&gt;
            {{ blog.date | date:'mediumDate' }}
        &lt;/div&gt;
    &lt;/h2&gt;
    &lt;p&gt;
        {{ blog.body }}
    &lt;/p&gt;
&lt;/div&gt;

<br/>

src/app/components/blog-item/blog-item.component.css

img {
    width: 100%;
    height: 200px;
    object-fit: cover;
    border-radius: 10px;
    border: 1px solid #000;
}
 
p {
    overflow: hidden;
    text-overflow: ellipsis;
    height: 36px;
}

<br/>

Now, we can update the BlogListComponent to use the BlogItemComponent as a child.

src/app/components/blog-list/blog-list.component.html

&lt;div class="list"&gt;
    &lt;app-blog-item class="card" *ngFor="let blog of blogs"&gt;&lt;/app-blog-item&gt;
&lt;/div&gt;

<br/>

Now, we need to be able to give the blog data from the BlogListComponent to the BlogItemComponent.

<br/>

Inter-component Interaction

To pass the blog data to the BlogItemComponent, we can simply add this code to the template:

src/app/components/blog-list/blog-list.component.html

&lt;div class="list"&gt;
    &lt;app-blog-item class="card" *ngFor="let blog of blogs"
        [blog]="blog"
    &gt;&lt;/app-blog-item&gt;
&lt;/div&gt;

<br/>

This will tell Angular that the BlogItemComponent is accepting a blog field as Input and we are passing each individual blog from the blogs list as the value. Now, we have to define the blog property in BlogItemComponent as an Input. We can do that by using the Input decorator.

src/app/components/blog-item/blog-item.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { Blog } from 'src/app/models/Blog';
 
@Component({
  selector: 'app-blog-item',
  templateUrl: './blog-item.component.html',
  styleUrls: ['./blog-item.component.css']
})
export class BlogItemComponent implements OnInit {
  @Input() blog?: Blog;
 
  constructor() {}
 
  ngOnInit(): void {
  }
}

<br/>

Now, our app should work again like before. But the difference now is if we wanted to add/change something about each item, we only need to update the BlogItemComponent which has a simpler property than the BlogListComponent.

Inversely, you can also send events from a child component to its parent. To demonstrate, we can make a simple button on the blog item that simply logs a string. We can bind an event to an element by doing the following:

src/app/components/blog-item/blog-item.component.html

&lt;div *ngIf="blog"&gt;
    &lt;div&gt;
        &lt;img *ngIf="blog.imageUrl" src="{{blog.imageUrl}}"/&gt;
        &lt;!-- if no image, put a default --&gt;
        &lt;img *ngIf="!blog.imageUrl" src="https://aliceasmartialarts.com/wp-content/uploads/2017/04/default-image.jpg"&gt;
    &lt;/div&gt;
    &lt;h2&gt;{{ blog.title | uppercase }}
        &lt;div class="sub-date"&gt;
            {{ blog.date | date:'mediumDate' }}
        &lt;/div&gt;
    &lt;/h2&gt;
    &lt;p&gt;
        {{ blog.body }}
    &lt;/p&gt;
 
    &lt;button style="background-color: red;color: white" (click)="clickButton()"&gt;Click me&lt;/button&gt;
&lt;/div&gt;

<br/>

And now, we need to define the clickButton method in our ts file.

src/app/components/blog-item/blog-item.component.ts (snippet)

...
export class BlogItemComponent implements OnInit {
  ...
  clickButton() {
    console.log('Button clicked from child!');
  }
}

<br/>

When we click on the button, we should see that it should have logged the string to the console.

<div> <img class="img-fluid" src="https://strapi-saperium.s3.ap-southeast-1.amazonaws.com/angular_workshop_output_1_018168a789.png" width="50%"> </div>

To send this event to its parent, we need to use EventEmitter and specify it as an Output using the Output decorator.

src/app/components/blog-item/blog-item.component.ts (snippet)

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
  ...
export class BlogItemComponent implements OnInit {
  ...
  @Output() onClick = new EventEmitter&lt;Blog&gt;();
  ...
  clickButton() {
    console.log('Button clicked from child!');
    this.onClick.emit(this.blog);
  }
}

<br/>

Now that we have set that up, we need to let BlogListComponent know what to do when the onClick property emits a value.

src/app/components/blog-list/blog-list.component.html

&lt;div class="list"&gt;
    &lt;app-blog-item class="card" *ngFor="let blog of blogs"
        [blog]="blog"
        (onClick)="blogClicked($event)"
    &gt;&lt;/app-blog-item&gt;
&lt;/div&gt;

<br/>

src/app/components/blog-list/blog-list.component.ts (snippet)

  blogClicked(blog: Blog) {
    console.log(`Blog ${blog.id} was clicked`);
  }

<br/>

Now if we click the button again, both the parent and the child should have received the event and we should see this on the console.

<div> <img class="img-fluid" src="https://strapi-saperium.s3.ap-southeast-1.amazonaws.com/angular_workshop_output_2_1936f2f198.png" width="50%"> </div>

<br/>

Services

Normally, we don’t want to hard-code values on the component level and we request for data from a database. So we will take out the values defined in BlogListComponent and first move them to a service.

As mentioned earlier, services allow for data sharing between components and that will be very useful if we already have a complicated app with many components. It also makes data management a lot easier. Now we will generate a service called BlogService, move blogs there and create a method that returns the list.

$ ng generate service [path-to-service]/[service-name]
$ ng generate service services/blog

<br/>

src/app/services/blog.service.ts

import { Injectable } from '@angular/core';
import { Blog } from '../models/Blog';
 
@Injectable({
  providedIn: 'root'
})
export class BlogService {
  blogs: Blog[] = [
    {
      id: 1,
      title: 'Blog Post 1',
      body: 'This is my first blog post',
      date: 1610953242000,
      imageUrl: 'https://static.demilked.com/wp-content/uploads/2018/03/5aaa1cc1d8480-funny-weird-wtf-stock-photos-1-5a39255d726bb__700.jpg'
    },
    {
      id: 2,
      title: 'Blog Post 2',
      body: 'This is my second blog post',
      date: 1610953242000
    },
    {
      id: 3,
      title: 'Blog Post 3',
      body: 'This is my third blog post',
      date: 1610953242000,
      imageUrl: 'https://static.demilked.com/wp-content/uploads/2018/03/5aaa1cce4180b-funny-weird-wtf-stock-photos-57-5a3bb7ba3c266__700.jpg'
    },
    {
      id: 4,
      title: 'Blog Post 4',
      body: 'This is my last blog post... for now!',
      date: 1610953242000
    },
  ]
 
  getBlogs(): Blog[] {
    return this.blogs;
  }
}

<br/>

Now, we can use this service in our BlogListComponent to get the blogs. We can use services by injecting them to our components as a dependency. To do that, we need to include them in the constructor. This pattern is called Dependency Injection and to learn more about it, you can read up here: Inversion of Control Containers and the Dependency Injection pattern.

src/app/components/blog-list/blog-list.component.ts (snippet)

import { BlogService } from 'src/app/services/blog.service';
  ...
  this.blogs: Blog[] = [];
  constructor(
    private blogService: BlogService
  ) { }
 
  ngOnInit(): void {
    this.blogs = this.blogService.getBlogs();
  }

<br/>

The HttpClient service and Observables

Now that we have moved our data to a service, it is time to talk about getting data from a backend/database. We can use the HttpClientModule provided by Angular to make HTTP requests to a server. For this exercise, we will be using an external API to get data for our blogs. To use the HttpClientModule, we need to include it in the imports in app.module.ts.

src/app/app.module.ts (snippet)

import { HttpClientModule } from '@angular/common/http';  
  ...
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],

<br/>

Then we can inject the HttpClient service in our BlogService.

src/app/services/blog.service.ts (snippet)

import { HttpClient } from '@angular/common/http';
...
export class BlogService {
  url = 'http://localhost:3000/blogs';
 
  constructor(
    private http: HttpClient
  ) { }

<br/>

Getting data from a server is an asynchronous operation because it takes time to send a request to a server and receive a response back, but we do not want to block our own app while waiting. Luckily, HttpClient passes values with the use of RxJS Observables. These give developers an easy technique to handle asynchronous operations. To learn more about asynchronous programming and Observables, you can refer to these documents: Asynchronous JavaScript - Learn web development | MDN, Using observables to pass values, Observable.

To make an HTTP GET request, use the get method from the HttpClient.

src/app/services/blog.service.ts (snippet)

import { Observable } from 'rxjs';
...
export class BlogService {
  url = 'http://localhost:3000/blogs';
 
  constructor(
    private http: HttpClient
  ) { }
 
  getBlogs(): Observable&lt;any&gt; {
    return this.http.get(`${this.url}`);
  }

<br/>

Next, we need to change how we handle this on the BlogListComponent side. Since getBlogs returns an Observable, we cannot assign it to an object of type Blog[]. To handle this, we use subscribe and pass a callback to it.

src/app/components/blog-list/blog-list.component.ts (snippet)

  ngOnInit(): void {
    this.blogService.getBlogs().subscribe((result) =&gt; {
      this.blogs = result;
    });
  }

<br/>

Now whenever http.get receives some data, the subscribe callback will run and will assign the response from the server to the blogs list.

<br/>

Routing

Normally in blogs you can take a closer look at a post when you click on it, but it will take you to a different page. Let us make a component for this page called BlogPageComponent.

$ ng generate component components/blog-page

<br/>

src/app/components/blog-page/blog-page.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Blog } from 'src/app/models/Blog';
import { BlogService } from 'src/app/services/blog.service';
 
@Component({
  selector: 'app-blog-page',
  templateUrl: './blog-page.component.html',
  styleUrls: ['./blog-page.component.css']
})
export class BlogPageComponent implements OnInit {
  blog: Blog;
 
  constructor() { }
 
  ngOnInit(): void {
  }
}

<br/>

src/app/components/blog-page/blog-page.component.html

&lt;div *ngIf="blog" id="blog-page"&gt;
    &lt;h2&gt;
        {{ blog.title | uppercase }}
        &lt;div class="sub-date"&gt;
            {{ blog.date | date:'mediumDate' }}
        &lt;/div&gt;
    &lt;/h2&gt;
    &lt;div id="img-container"&gt;
        &lt;img *ngIf="blog.imageUrl" src="{{blog.imageUrl}}"/&gt;
        &lt;!-- if no image, put a default --&gt;
        &lt;img *ngIf="!blog.imageUrl" src="https://aliceasmartialarts.com/wp-content/uploads/2017/04/default-image.jpg"&gt;
    &lt;/div&gt;
 
    &lt;p class="content"&gt;
        {{ blog.body }}
    &lt;/p&gt;
&lt;/div&gt;

<br/>

src/app/components/blog-page/blog-page.component.css

#blog-page {
    margin: 20px auto;
    width: 75%;
}
 
a {
    color: #002bff;
}
 
.content {
    margin: 20px auto;
    width: 65%;
}
 
#img-container {
    margin: 0 auto;
    border: 1px #000 solid;
    border-radius: 10px;
    width: fit-content;
    max-height: 300px;
    display: flex;
}

img {
    border-radius: 10px;
    width: 100%;
    object-fit: none;
}

<br/>

And now, we can talk about routing. Make sure to have the AppRoutingModule in the app.module.ts. Open app-routing.module.ts and notice that there is an empty routes array.

src/app/app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { BlogListComponent } from './components/blog-list/blog-list.component';
import { BlogPageComponent } from './components/blog-page/blog-page.component';
 
const routes: Routes = [];
 
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

<br/>

We can configure our routes here. We will add 2 simple routes:

  • if the path is empty, we want to route to the list;
  • if the path has the id, we want to route to the blog post with that id.

Add these to the routes array.

src/app/app-routing.module.ts (snippet)

import { BlogListComponent } from './components/blog-list/blog-list.component';
import { BlogPageComponent } from './components/blog-page/blog-page.component';
 
const routes: Routes = [
  { path: '', component: BlogListComponent },
  { path: ':id', component: BlogPageComponent }
];

<br/>

We also need to change how our components are loaded in AppComponent. In app.component.html, we will use the special router-outlet element and this will be responsible for rendering the correct components depending on the current route.

src/app/app.component.html

&lt;app-header&gt;&lt;/app-header&gt;
&lt;router-outlet&gt;&lt;/router-outlet&gt;

<br/>

Now, if we go to path http://localhost:4200/1, we should see that the list is no longer rendered. But we will still get an error because the blog is still empty. We need to make another HTTP GET request to get the post with id of 1.

We can use the ActivatedRoute service to get the current url of the app (so that we can get the appropriate id) and make a new method in our BlogService to pass an id and get a single post.

src/app/services/blog.service.ts (snippet)

export class BlogService {
  ...
  getBlog(id: number): Observable&lt;any&gt; {
    return this.http.get(`${this.url}/${id}`);
  }
}

<br/>

src/app/components/blog-page/blog-page.component.ts (snippet)

import { ActivatedRoute } from '@angular/router';
import { BlogService } from 'src/app/services/blog.service';
...
export class BlogPageComponent implements OnInit {
  blog: Blog;
 
  constructor(
    private blogService: BlogService,
    private router: ActivatedRoute
  ) { }
 
  ngOnInit(): void {
    const url = this.router.snapshot.paramMap.get('id');
    if (url) {
      const id = parseInt(url);
      this.blogService.getBlog(id).subscribe((result) =&gt; {
        this.blog = result;
      });
    }    
  }
}

<br/>

Now, we should correctly see our BlogPageComponent in action.

<div> <img class="img-fluid" src="https://strapi-saperium.s3.ap-southeast-1.amazonaws.com/angular_workshop_route_1_ec8a822aa8.png" width="100%"> </div>

<br/>

Next let us add links to our app to help us navigate. Add a “Go back” link to the BlogPageComponent that takes us back to the list. We can add in a routerLink so that we can use the path names we used in app-routing.module.ts to add the destination.

src/app/components/blog-page/blog-page.component.html

&lt;div *ngIf="blog" id="blog-page"&gt;
    &lt;h2&gt;
        {{ blog.title | uppercase }}
        &lt;div class="sub-date"&gt;
            {{ blog.date | date:'mediumDate' }}
        &lt;/div&gt;
    &lt;/h2&gt;
 
    &lt;div id="img-container"&gt;
        &lt;img *ngIf="blog.imageUrl" src="{{blog.imageUrl}}"/&gt;
        &lt;!-- if no image, put a default --&gt;
        &lt;img *ngIf="!blog.imageUrl" src="https://aliceasmartialarts.com/wp-content/uploads/2017/04/default-image.jpg"&gt;
    &lt;/div&gt;
 
    &lt;p class="content"&gt;
        {{ blog.body }}
    &lt;/p&gt;
    &lt;a routerLink="/"&gt;Go back&lt;/a&gt;
&lt;/div&gt;

<br/>

Likewise, we will also add links to the list to take us to the blog pages.

src/app/components/blog-item/blog-item.component.html

&lt;div *ngIf="blog"&gt;
    &lt;div&gt;
        &lt;img *ngIf="blog.imageUrl" src="{{blog.imageUrl}}"/&gt;
        &lt;!-- if no image, put a default --&gt;
        &lt;img *ngIf="!blog.imageUrl" src="https://aliceasmartialarts.com/wp-content/uploads/2017/04/default-image.jpg"&gt;
    &lt;/div&gt;
    &lt;h2&gt;{{ blog.title | uppercase }}
        &lt;div class="sub-date"&gt;
            {{ blog.date | date:'mediumDate' }}
        &lt;/div&gt;
    &lt;/h2&gt;
    &lt;p&gt;
        {{ blog.body }}
    &lt;/p&gt;
 
    &lt;div class="link"&gt;
        &lt;a routerLink="/{{blog.id}}"&gt;See more...&lt;/a&gt;
    &lt;/div&gt;
&lt;/div&gt;

<br/>

src/app/components/blog-item/blog-item.component.css

img {
    width: 100%;
    height: 200px;
    object-fit: cover;
    border-radius: 10px;
    border: 1px solid;
}
 
p {
    overflow: hidden;
    text-overflow: ellipsis;
    height: 36px;
}
 
.link {
    text-align: right;
}
 
a {
    color: #1f1ff3;
}

<br/>

Finally, add a link to the header as well.

src/app/components/header/header.component.html

&lt;div id="header"&gt;
    &lt;h1&gt;&lt;a routerLink="/"&gt;blogg.io&lt;/a&gt;&lt;/h1&gt;
&lt;/div&gt;

<br/>

src/app/components/header/header.component.css

#header {
    margin-bottom: 30px;
    padding: 20px;
    background-color: #353230;
}
 
h1 {
    margin-top: 0;
    margin-bottom: 0;
}
 
a {
    color: white;
}
 
a:hover {
    color: rgb(129, 97, 36)
}

<br/>

We have just completed our simple blog application and learned all the basics of designing web applications with Angular!

<div> <img class="img-fluid" src="https://strapi-saperium.s3.ap-southeast-1.amazonaws.com/angular_workshop_5_e093bf55ec.png" width="100%"> </div>