Angular 6 is available, so I have decided to bring my instructions about Consuming a REST Service with Angular to the next level. In this step-by-step hello-world-style tutorial, you will be lead through following steps:

  • installation of Angular CLI v6 in a CentOS docker container
  • retrieval of HTML code data from the WordPress API
  • display of the retrieved HTML code in a browser

Similar to the code of the latest 6.0 Tour of Heroeswe will use pipes to pass through the code. To keep it simple, error handling is not yet part of the tutorial.

Note: Since I have not tested, whether the new tour-of-heroes-style code works also with versions below 6.0, I have kept the original Angular 4.3+ instructions in the Appendix below.

Contents

Phase 1: Install Angular in a Docker Container

We will install Angular in a CentOS Docker container. You may skip the Docker part and can perform the same steps on a real CentOS server if you wish.

Step 1.1 (optional): Run a CentOS Docker Container

mkdir angular-on-centos; cd angular-on-centos
docker run -it -p 4200:4200 -v $(pwd):/localdir centos bash

Step 1.2: Install NodeJS 10.x and Angular CLI

A recent v10.x version of NodeJS can be installed with the following command (see https://nodejs.org/en/download/package-manager/ for details):

(container)# curl --silent --location https://rpm.nodesource.com/setup_10.x | bash - && yum install -y nodejs 

Note: the standard nodejs installation way via EPEL_Release (yum install -y epel-release && yum install -y nodejs)  will install a too old version of Nodejs, currently

Now is the time to install Angular CLI:

(container)# npm install -g @angular/cli

Now let us verify the version of angular and its components:

(container)# ng -v
     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 6.0.0
Node: 10.1.0
OS: linux x64
Angular:
...

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.6.0
@angular-devkit/core         0.6.0
@angular-devkit/schematics   0.6.0
@schematics/angular          0.6.0
@schematics/update           0.6.0
rxjs                         6.1.0
typescript                   2.7.2

Okay, at the moment, Angular CLI 6.0.0 is the standard version.

Step 1.3 (optional): Install GIT

You may want to keep track of the changes you perform on the source code. For that, we can install GIT as follows:

curl https://raw.githubusercontent.com/oveits/bootstrap-centos/master/2_update-git-centos.sh | bash

With that, a relatively recent version of GIT is installed

Note: here again, the CentOS standard installation via EPEL-Release (yum install -y epel-release && yum install -y git) leads to a quite old installed version and not nice to work with.

Step 1.3: Create new Project

Step 1.3.1: Create the Project

Now let us create a project:

cd /localdir
ng new my-project-name
cd my-project-name

Depending on the speed of your Internet connection, the second command might take some minutes.

Step 1.3.2 Check the Version of the Project

grep @angular/core package.json 
 "@angular/core": "^6.0.0",

We can see that angular has generated a v6.0 Angular project in this case.

Step 1.4: Start the Service

With the following command, we start the service:

$ ng serve --host 0.0.0.0 --port=4200

The host option is needed if you access the service from outside. The port option is only needed if you use a different port than the default port 4200.

Step 1.5: Check the App in a Browser

Let us open a browser and head over to http://<host>:4200. In your case, the host might be ‘localhost’. Since I was running the application on a cloud server, I had to enter the IP-Address of the server instead:

Note: you may want to initialize GIT at this point in order to track further changes:

cd <project-root>
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
git init
git add .
git commit -am'initial commit after ng new'

Phase 2: Install the REST Consumer Service

In this phase, we will see, how easy it is in Angular 4.3+ to 6 to retrieve and display data from a REST API.

Step 2.1: Add HTTP Client Module

First, we need to tell Angular that we will use the HttpClientModule. For that, we edit src/app/app.module.ts (added parts in blue).

Note: since the HttpModule is deprecated now, we have replaced the module by the more modern HttpClientModule.

Now let us begin to edit the files (new parts in blue):

src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';

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

With that, we have told Angular, that we will make use of the HttpClientModule (requires Angular 4.3+).

src/app/app.component.ts

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'app';
  restItems: any;
  restItemsUrl = 'https://public-api.wordpress.com/rest/v1.1/sites/vocon-it.com/posts';

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.getRestItems();
  }

  // Read all REST Items
  getRestItems(): void {
    this.restItemsServiceGetRestItems()
      .subscribe(
        restItems => {
          this.restItems = restItems;
          console.log(this.restItems);
        }
      )
  }

  // Rest Items Service: Read all REST Items
  restItemsServiceGetRestItems() {
    return this.http
      .get<any[]>(this.restItemsUrl)
      .pipe(map(data => data));
  }
}

Here we have used the more modern pipe(map…) way of implementing the service. Moreover, we have separated the actual service into its own function, so it is easier to separate this part into its own service file later.

The OnInit() function will call the service upon reload of the page and we expect that an HTTP GET call is sent to the REST Service. We will explore this in the next step.

Step 2.2 (optional): View the REST call in the Browser’s Network Panel

The REST call and its answer can be seen in the network panel that is available in the debug mode of a Chrome browser (press F12):

The content of the response body is sent to the console via the console.log call in the subscribe method. Because of pagination on the WordPress REST API, we can see the latest 20 of the available 72 public posts of this blog:

We also can see that the REST API has provided us with title and content of all of the 20 posts:

We now want to make the content visible.

Step 2.3: Display REST Data in the Browser

Step 2.3.1: Display List of Blogs

Now let us display the blog posts. We remove all content of src/app/app.component.html and replace it with the following content:

<h1>Blog Posts</h1>
<div *ngIf="undefined === restItems">Loading...</div>
<div *ngIf="undefined !== restItems">
  <ul>
    <li *ngFor="let post of restItems['posts']" [innerHTML]="post.title">
    </li>
  </ul>
</div>

Or here, see the same as a screenshot, since WordPress does happen to mess up with HTML code from time to time when switching between visual and text mode:

Within the HTML template, we loop through the array of posts that can be found in the ‘posts’ section of the REST response. With the *ngIf statements, we make sure that we do not try to read ‘posts’ from restItems before they have not yet been read successfully from the API. Moreover, we show a “Loading…” information while waiting for the data.

In the HTML template, we have chosen an innerHTML binding, so the HTML content of the titles is displayed correctly:

Step 2.3.2: Display a single Post Content in the Browser

We also can access the content of the of the blogs, as we can show in this little example:
<h1>Blog Posts</h1>

<div *ngIf="undefined === restItems">Loading...</div>

<div *ngIf="undefined !== restItems">
  <ul>
    <li *ngFor="let post of restItems['posts']" [innerHTML]="post.title"
        (click)="selectedContent='<h1>' + post.title + '</h1>' + post.content">
    </li>
  </ul>
</div>
<div [innerHTML]="selectedContent"></div>

With that, we the content of the post is shown at the bottom after clicking the entry:

Step 2.4: Fix the Table of Contents

My WordPress page will return a table of contents (TOC) because I have installed TOC plugin. However, you will note that the links to the headlines do not work. We will see the reason in the debug console (press F12) of the Chrome browser:

WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss).

The problem and the workaround is described in my blog post “Angular 4: Automatic Table of Contents – Part 2: Adding Links“.

A quick&dirty way of resolving the problem is to add/replace following lines in ‘src/app/app.component.html’:

...
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
...

constructor(private http: HttpClient, public sanitizer: DomSanitizer) {}

After that, we only need to replace

<div [innerHTML]="selectedContent"></div>

by

<div *ngIf="undefined !== selectedContent" [innerHTML]="sanitizer.bypassSecurityTrustHtml(selectedContent)"></div>

After that, the links in the HTML Content work fine. After clicking e.g.

Step 3 (optional): Refactoring (1): Move Service to its own File

Step 3.1: Create Service File

For increasing the re-usability, it makes sense to separate the service into its own file. For that, we add a service.ts file like follows:

src/app/app.service.ts

 
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';

@Injectable()
export class AppService implements OnInit {

  protected url : string = 'https://public-api.wordpress.com/rest/v1.1/sites/vocon-it.com/posts';

  constructor(private http: HttpClient, public sanitizer: DomSanitizer) {}

  // Rest Items Service: Read all REST Items
  getAll() {
    return this.http
      .get<any[]>(this.url)
      .pipe(map(data => data));
  }

}

Step 3.2: Strip unneeded Code from the Component File

The corresponding code can be stripped from the component file:

src/app/app.component.ts

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { AppService } from './app.service.ts';


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'app';
  restItems: any;
  url = 'https://public-api.wordpress.com/rest/v1.1/sites/vocon-it.com/posts';

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.getRestItems();
  }

  // Read all REST Items
  getRestItems(): void {
    this.appService.getAll()
      .subscribe(
        restItems => {
          this.restItems = restItems;
          console.log(this.restItems);
        }
      )
  }

  // Rest Items Service: Read all REST Items
  restItemsServiceGetRestItems() {
    return this.http
      .get<any[]>(this.restItemsUrl)
      .pipe(map(data => data));
  }
}

Step 3.3: Announce the new Provider in the Module File

Now we need to let angular know that a new service provider is present:

src/app/module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';
import { AppService } from './app.service';

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

Step 3.4: Test the Result

The changes should lead to an unchanged output:

Yes, this works fine.

Step 4 (optional): Refactoring (2): Moving Functionality from HTML template to the Component

You might have noticed that we have used terms like

restItems['posts']

within the HTML template. Since restItems are undefined, as long as the REST service has not sent a response, we were forced to sanitize the code by adding an ngIf clause:

<div *ngIf="undefined !== restItems">
   ... restItems['posts'] ...
</div>

The main purpose of the HTML template is to visualize data, and not to validate and extract data. Therefore, we should move the code to the service and/ component, if possible. Let us do so now.

Step 4.1: Allow Service to extract Posts

Let us extract the posts array in the service:

src/app/app.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';

@Injectable()
export class AppService {

  protected url : string = 'https://public-api.wordpress.com/rest/v1.1/sites/vocon-it.com/posts';

  constructor(private http: HttpClient) {}

  // Rest Items Service: Read all REST Items
  getAll() {
    return this.http
      .get<any[]>(this.url)
      .pipe(map(data => data['posts']));
  }

}

With that change, we expect that the data sent by the service is an array of posts. Therefore, let us rename the corresponding variable in the component:

src/app/app.component.ts

import { Component, OnInit } from '@angular/core';
import { map } from 'rxjs/operators';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { AppService } from './app.service';


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  posts: any;

  constructor(private appService : AppService, public sanitizer: DomSanitizer) {}

  ngOnInit() {
    this.getRestItems();
  }

  // Read all REST Items
  getRestItems(): void {
    this.appService.getAll()
      .subscribe(
        posts => {
          this.posts =
            posts.map(
              post => {
                return {
                  "title": this.sanitizer.bypassSecurityTrustHtml(post.title),
                  "content": this.sanitizer.bypassSecurityTrustHtml(post.content)
                }
            });
          console.log(this.posts);
        }
      )
  }
}

Moreover, we have moved the sanitizer code from the HTML template to the component and we have stripped all information, we do not need, from posts.

With this change, the HTML template can be simplified:

src/app/app.component.html

<h1>Blog Posts</h1>

<div *ngIf="undefined === posts">Loading...</div>

<ul>
  <li *ngFor="let post of posts" [innerHTML]="post.title" (click)="selectedPost = post">
  </li>
</ul>

<div *ngIf="selectedPost">
  <h1 [innerHTML]="selectedPost.title"></h1>
  <div [innerHTML]="selectedPost.content"></div>
</div>

While the result is not changed at all, we have moved typescript functionality from HTML template the the typescript files, where it belongs to (we might find better places to move that code to, though).

Step 5 (optional): Interceptors

Interceptors are a new cool feature of the HttpClientModule (available since Angular 4.3). With an interceptor, we can hide the REST API specifics into a REST API specific interceptor file. As most tutorials do, let us begin with a NOOP interceptor that does not do anything:

Step 5.1: Create Interceptor File

As most tutorials do (e.g. on angular.io), let us begin with a NOOP interceptor that does not do anything. For that, let us create following file:

src/app/wordpress-api.interceptor.ts

import { Injectable } from '@angular/core';
import {
  HttpInterceptor,
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpResponse
} from '@angular/common/http';

import { Observable } from 'rxjs';

@Injectable()
export class WordPressApiInterceptor implements HttpInterceptor {
    intercept(
        request: HttpRequest,
        next: HttpHandler
    ): Observable<HttpEvent> {
        console.log('WordpressApiInterceptor was called');
        return next.handle(request);
    }
}

As described in the angular.io documentation, each interceptor needs to define an intercept() function, which returns a next.handle(request).

Step 5.2: Activate Interceptor

The interceptor needs to be activated in the App Module:

src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { WordPressApiInterceptor } from './wordpress-api.interceptor';

import { AppComponent } from './app.component';
import { AppService } from './app.service';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    HttpClientModule,
    BrowserModule
  ],
  providers: [
    AppService,
    { provide: HTTP_INTERCEPTORS, useClass: WordPressApiInterceptor, multi: true }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

The ‘multi: true’ is needed, because HTTP_INTERCEPTORS is an array of values.

Step 5.3: Verify that the Interceptor is called

We had added a console log in the interceptor, that now can be observed in the console of a chrome browser after pressing F12:

Moreover, we an debug the code by navigating to Webpack –> . –> src –> app –> wordpress-api.interceptor.ts and setting a breakpoint:

Step 5.4: Adapt the Interceptor for outgoing Requests

In this section, we will show how to add a custom header in outgoing requests.

Note: in the pre-flight OPTION, WordPress tells me that they accept only following headers: Authorization, Content-Type, X-Jetpack. Let us manipulate the Content-Type for this hello-world…

We can add an authentication header like follows:

src/app/wordpress-api.interceptor.ts

import { Injectable } from '@angular/core';
import {
  HttpInterceptor,
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpResponse
} from '@angular/common/http';

import { Observable } from 'rxjs';


@Injectable()
export class WordPressApiInterceptor implements HttpInterceptor {

    intercept(
        request: HttpRequest,
        next: HttpHandler
    ): Observable<HttpEvent> {
        console.log('WordpressApiInterceptor was called');

        const clonedRequest = request.clone({
            headers: request.headers.set('Content-Type', 'BLABLUB')
        });
        return next.handle(clonedRequest);
   }
}

Note: requests are read-only, so we need to clone the original request and hand over the clone via next.handle instead of changing the request. We are choosing an easily recognizable BLABLUB Content-Type, which can be seen immediately in the network panel of the browser’s debug mode (F12):

This shows, how we can manipulate headers in outgoing requests.

Step 5.5: Manipulate incoming Responses

In this section, we will learn how to manipulate the bodies on incoming responses.

Above, we have seen that the WordPress API is returning an answer of the format

{ posts: [ ... ] }

To extract the posts, we had used following map in the service:

map(data => data['posts'])

The target of this section is to move this interface specific manipulation to the interceptor. For that, we replace following line in the interceptor file:

src/app/wordpress-api.interceptor.ts

        return next.handle(clonedRequest);
        return next.handle(clonedRequest).pipe(map(event => {
           if (request.method === 'GET' && event instanceof HttpResponse) {
                console.log("Response Interceptor for GET");
                return event.clone({ body: event.body['posts']});
           }
        }));

Note: The if-clause is mandatory for making sure the body is manipulated on GET responses only. If the if-clause was missing here, the interceptor would try to manipulate the Http Packet for outgoing requests as well.

We now can simplify the service by removing the posts extraction, since this work is now done by the interceptor:

src/app/app.service.ts

      .pipe(map(data => data['posts']));

With that, the posts are loaded successfully again:

As we have seen above, the interceptor code is more complex than the code we were able to remove from the service, but it helps to keep service clean from any API-specifics, making the service code re-usable for other APIs.

References

Summary

In this hello world style step-by-step guide, we have learned how to

  • install an Angular 5 project using the Angular CLI and
  • consume the WordPress API for retrieving and displaying a blog post title and HTML content.
  • tell Angular to trust the “innerHTML” content retrieved from the API

Below: Same on Angular 4.3 to 5.x

Fore reference, I have kept the upcoming post version that has been tested with Angular v5.2 below…

In this step-by-step tutorial, we will learn how to consume a REST Service with Angular 5.2. We will perform following steps:

  • install Angular using the Angular CLI
  • create an Angular single page application that is consuming a RESTful Web Service with Angular
  • make sure that the HTML content is trusted and fully functional.

Updates:

  • 2018-05-12
    • tested with Angular 6. We are now using pipes similar to the latest Tour of Heros.
  • 2018-04-06
    • Updated from deprecated HttpModule (@angular/http) to new HttpClientModule (@angular/common/http)
  • 2018-03-24
    • tested with Angular 5.2
    • included information about how to retain the full HTML functionality of the content retrieved via  the REST API

Consuming a REST Service with Angular

At the end of this tutorial, we will have created a simple Angular page that is displaying the contents of a previous blog post on WordPress. The content has been retrieved from the WordPress REST API in JSON format. The title and content will be displayed:

The rest of the article is organized into two phases:

  1. Installing Angular CLI on CentOS
  2. Programming the Angular REST Client

Let us start:

Phase 1: Install Angular CLI on CentOS

Step 1.0: Install a Docker Host

This time, we will need to use a real Docker host: i.e., different from our last Angular Hello World Quickstart post, this time it is not possible to use Katacoda as our Docker Host playground. We will install Angular CLI, and this requires more Memory than is provided by Katacoda. If you have no access to a Docker host yet and if you are working on a Windows machine, you might want to follow the instructions found here; search for the term “Install a Docker Host”. It describes step by step how to install a Docker host on Virtualbox using Vagrant. I have made good experiences with that approach. For other operating systems, you may want to consult the official Docker installation instructions.

Note: the official Docker installation instructions for Windows might work as well, but years ago, when I had started with Docker, the official solution had urged me to write a blog post “Why is it so hard to install Docker on Windows”). Those were the times with boot2docker. However, the situation has improved from what I have heard from other colleagues that have started with Docker more recently. Still, I like the Vagrant approach a lot, and I will stick to that solution.

Step 1.1: Start CentOS Container

Let us create a Docker CentOS container on a Docker host:

docker run -it -p 4200:4200 centos bash

We have mapped TCP container port 4200 to the Docker host port 4200 since we want to access the service on this port from outside.

Persistent alternative (optional): to persist the Angular project we will be creating on the Docker host, it makes sense to map a  volume from Docker host to Docker container:

mkdir angular-5-on-centos; cd angular-5-on-centos
docker run -it -p 4200:4200 -v $(pwd):/localdir centos bash

Step 1.2: Install NodeJS and Angular CLI

On the container:

(container)# yum install epel-release && yum install -y nodejs
(container)# npm install -g @angular/cli@1.7.3
(container)# yum install -y git

Note: the second command has been introduced recently, since the latest epel-release is missing the http parser library. See SvennD’s blog for more details.

Note: the git install command has been introduced to allow the creation of a .gitignore file in the next step. The alternative git installation via curl will install a newer version of GIT, which I prefer compared with the old version that comes with the epel-release.

In order to verify the installation, let us check the version of Angular CLI and node:

(container)# ng -v

    _                      _                 ____ _     ___
   / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
  / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
 / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
/_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
               |___/

Angular CLI: 1.7.3
Node: 6.12.3
OS: linux x64
Angular:
...

Step 1.3: Create new Project

Step 1.3.1: Create the Project

Now let us create a project:

cd /localdir
ng new my-project-name
cd my-project-name

Depending on the speed of your Internet connection, the second command might take some minutes.

We have not installed GIT yet, so we will get following error message at the end:

/bin/sh: git: command not found
Project ‘my-project-name’ successfully created.

Step 1.3.2 Check the Version of the Project

grep @angular/core package.json
 "@angular/core": "^5.2.0",

We can see that angular has generated a v5.2 Angular project in this case.

Step 1.4: Start the Service

$ ng serve --host 0.0.0.0
** NG Live Development Server is listening on 0.0.0.0:4200, open your browser on http://localhost:4200/ **
Date: 2018-03-24T14:24:53.729Z
Hash: 5aab41746ecad14f6d3b
Time: 17472ms
chunk {inline} inline.bundle.js (inline) 3.85 kB [entry] [rendered]
chunk {main} main.bundle.js (main) 17.9 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js (polyfills) 549 kB [initial] [rendered]
chunk {styles} styles.bundle.js (styles) 41.5 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js (vendor) 7.42 MB [initial] [rendered]

webpack: Compiled successfully.

Step 1.5: Connect to the Service

If you are running the docker host locally, or if your local port 4200 is mapped to your docker host’s port 4200, we get:

Phase 2: Programming the Angular REST API Client

In this phase, we will see, how easy it is in Angular 2 to 5 to retrieve and display data from a REST API.

Step 2.1: Add HTTP Client Module

First, we need to tell Angular that we will use the HttpClientModule. For that, we edit src/app/app.module.ts (added parts in blue).

Note: since the HttpModule is deprecated now, we have replaced the module by the more modern HttpClientModule.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';

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

Step 2.2: Configure Component to use HTTP

In a second step, we configure Edit src/app/app.component.ts:

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  public title : String = 'Loading title...';
  public content : String = '';

  constructor(private _http: HttpClient) {
  }

  ngOnInit() {
     this._http.get('https://public-api.wordpress.com/rest/v1.1/sites/vocon-it.com/posts/3078')
                 .subscribe(data => {
                        this.title = data.title;
                        this.content = data.content;
                        console.log(data);
                });
  }
}

Step 2.3: Adapt HTML Template

Remove all content of src/app/app.component.html and replace it with the following content:

<h2 style="color:blue;">The following content has been retrieved from the WordPress REST API:</h2>
<h1 style="font-size: 250%;" [innerHTML]="title">Loading Title...</h1>
<div [innerHTML]="content">Loading Content...</div>

(Sometimes WordPress seemed to have a problem displaying the content correctly, so I post it as a screenshot in addition to the text that might disappear when trying to save the page)

After that, we will see that the browser is displaying following content:

Step 2.4: Fix the Table of Contents

My WordPress page will return a table of contents (TOC) because I have installed TOC plugin. However, you will note that the links to the headlines do not work. We will see the reason in the debug console (press F12) of the Chrome browser:

WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss).

The problem and the workaround is described in my blog post “Angular 4: Automatic Table of Contents – Part 2: Adding Links“.

The problem arises from the fact that Angular does not trust dynamically assigned innerHTML per default. It will remove any IDs found in the content. We can see this easily in the in the elements section of the Chrome browser after entering debug mode (F12):

Angular has removed the ID of the span element. Hence, the links to those IDs will not work.

To fix this problem, we have to tell Angular explicitly, that it should trust the innerHTML content. This is done in the component as follows (changes in blue):

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  public title : SafeHtml|String = 'Loading title...';
  public content : SafeHtml|String = 'Loading content...';

  constructor(private _http: HttpClient, private _sanitizer: DomSanitizer) {
  }

  ngOnInit() {
     this._http.get('https://public-api.wordpress.com/rest/v1.1/sites/vocon-it.com/posts/3078')
                 .subscribe(data => {
                        this.title = this._sanitizer.bypassSecurityTrustHtml(data.title);
                        this.content = this._sanitizer.bypassSecurityTrustHtml(data.content);
                        console.log(data);
                });
  }
}

We check again the element in the debug mode of the chrome browser (F12): the IDs are present now:

With that, the links work as expected: When clicking the table of contents link, the browser will jump to the corresponding headline:

Summary

In this hello world style step-by-step guide, we have learned how to

  • install an Angular 5 project using the Angular CLI and
  • consume the WordPress API for retrieving and displaying a blog post title and HTML content.
  • tell Angular to trust the “innerHTML” content retrieved from the API

Next steps

  • Retrieve a list of Blog Posts instead of a single post only.
    • Figure out to handle the fact that retrieving all posts takes a lot of time initially; the WordPress REST API does not seem to allow to retrieve the list of titles only. Do we need to create an improved REST service that is quicker and allows to
  • The REST client can be separated in its own service as shown in this article.

Appendix: Exploring the WordPress REST API

The WordPress API can be explored via the WordPress.com REST API console.

You need to login via  and your WordPress.com credentials.

There seem to be several types and versions of APIs:

  • WP.COM API
    • v1.1 (default)
    • v1
  • WP REST API
    • wp/v2
    • wpcom/v2

In this Proof of Concept / Hello World, we are using the WP.COM API V1.1 to retrieve the content of a single WordPress blog post of mine:

https://public-api.wordpress.com/rest/v1.1/sites/vocon-it.com/posts/3078

However, we can also use the WP REST API wp/v2:

https://public-api.wordpress.com/wp/v2/sites/oliverveits.wordpress.com/posts/3078

Because of the organization of the wp/v2 result, we need to exchange data.title by data.title.rendered and data.content by data.content.rendered in the HTML template, though.

Note: wp/v2 API does not seem to work with our new primary domain vocon-it.com. We have changed the primary stite from oliverveits.wordpress.com to vocon-it.com a week ago.

References:

3 comments

  1. Thx for the blog. I’m new in Angular and I’m reading a lot. Just a hint for your audience. It does not work fine when the data are to long or takes to much to load. So I change it to

    @ViewChild(‘titleData’) titleData: ElementRef;
    @ViewChild(‘contentData’) contentData: ElementRef;

    constructor(private _http: Http) {
    this.getMyBlog();

    }

    private getMyBlog() {
    return this._http.get(‘https://public-api.wordpress.com/rest/v1.1/sites/oliverveits.wordpress.com/posts/3078’)
    .map((res: Response) => res.json())
    .subscribe(subscribedData => {
    this.data = subscribedData;
    this.loadTitle(subscribedData.title);
    this.loadContent(subscribedData.content);
    console.log(this.data);
    });
    }

    private loadTitle(titleData) {
    this.titleData.nativeElement.innerHTML = titleData;
    }

    private loadContent(contentData) {
    this.contentData.nativeElement.innerHTML = contentData;
    }
    }

    And that works fine.

    1. Hi Mike,
      thanks a lot for sharing. Which URL did you use to reproduce the problems with too long responses? Where did you find your solution?
      Thanks,
      Oliver

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.