0

Angular 4: Automatic Table of Contents – Part 2: Adding Links


In this short blog post, we will learn how to enrich the table of contents (we had added in the previous blog post) with links to the corresponding headline. The end result will look similar to the following picture and a click on one of the links will cause the browser to jump to the corresponding headline:

The method will be applied to dynamic content retrieved from the WordPress REST API. This poses some extra challenges caused by Angular’s security policies: per default, headline IDs are stripped, preventing us to successfully reference those IDs in the links. We will show below, how to make use of the SafeHtml module to successfully face those challenges.

Step 0: Download the Code and start and connect to the Server

Even though this is no prerequisite, I recommend to perform the steps within a Docker container. This way I can provide you with a Docker image that has all necessary software installed.

Fore more details on how to install a Docker host on Windows using Vagrant and Virtualbox, please see Step 1 of my Jenkins tutorial.

On a Docker host we run

(dockerhost)$ mkdir toc; cd toc
(dockerhost)$ docker run -it -p 8001:8000 -v $(pwd):/localdir oveits/angular_hello_world:centos bash
(container)# git clone https://github.com/oveits/ng-universal-demo
(container)# cd ng-universal-demo
(container)# git checkout 8b3948

Now we are up and running and we can start the server:

(container)# npm run watch &
(container)# npm run server

With that, any change of one of the files will cause the server to reload.

Open a browser and head to localhost:8001/blog

This is the end situation of the previous blog post: there is a table of contents, but the links to the headlines are missing. Those are the ones, will add today.

Note: if you are using Vagrant with a Docker host on a VirtualBox VM (as I do), per default, there is only a NAT-based interface and you need to create port-forwarding for any port you want to reach from outside (also the local machine you are working on is to be considered as outside). In this case, we need to add an entry in the port forwarding list of VirtualBox to map from Windows port 8001 to the VM’s port 8001 (in our case).

Step 1: Add IDs to the Headlines

First we need to make sure that each Headline has a unique ID we later can reference. For that, we assign some string “id394752934579″ concatenated by an increasing id_suffix within the getToc function we had defined in the previous blog post. This way we can be quite sure that the auto-created ID is unique on the page:

  private getToc(content: any) {
     // create div for holding the content
     var contentdiv = document.createElement("div");
     contentdiv.innerHTML = content;

     // create an array of headlines:
     var myArrayOfHeadlineNodes = [].slice.call(contentdiv.querySelectorAll("h1, h2"));

...

     // will be appended to the node id to make sure it is unique on the page:
     var id_suffix = 0;

     // loop through the array of headlines
     myArrayOfHeadlineNodes.forEach(
       function(value, key, listObj) {

...

           // if headline has no id, add a unique id
           if ("" == value.id) {
               value.id = "id394752934579" + ++id_suffix;
           }

...
       }
     );

...

     return(toc.innerHTML);
  }

Step 2: Apply the changed IDs to the dynamically retrieved Content

Since the myArrayOfHeadlineNodes is a list of references to the contentdiv’s headlines, the IDs of the headlines within the contentdiv variable has been manipulated in the previous step. However, the class variable “content” is unaffected by this change. Therefore, we need to apply the new enriched content to the class variable:

import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
...
  private getToc(content: any) {
     // create div for holding the content
     var contentdiv = document.createElement("div");
     contentdiv.innerHTML = content;

     // create an array of headlines:
     var myArrayOfHeadlineNodes = [].slice.call(contentdiv.querySelectorAll("h1, h2"));

...

     // update the content with the changed contentdiv, which contains IDs for every headline
     //   note that we need to use the saniztizer.bypassSecurityTrustHtml function in order to tell angular
     //   not to remove the ids, when used as [innerHtml] attribute in the HTML template
     this.content = this.sanitizer.bypassSecurityTrustHtml(contentdiv.innerHTML);

     return(toc.innerHTML);
  }

Just before returning, we write back the changed contentdiv variable’s content to the class variable this.content. But why did we apply this complicated bypass Security function?

 this.content = this.sanitizer.bypassSecurityTrustHtml(contentdiv.innerHTML);

Note that we apply the content as innerHTML in the HTML template (src/app/+blog/blog.module.html) like follows:

If the content is just a string containing HTML code, Angular will strip IDs and names from the innerHTML as a security measure. However, if we use the green saniziter code above instead, we explicitly tell Angular that it should trust the content, preventing Angular from stripping the IDs we are so keen on.

Step 3: Verify changed IDs of the Content’s Headlines

Unfortunately, the usage of “document” is not compatible with server-side rendering, as we have pointed out in the previous blog post. Therefore, we will not find the IDs in the server-provided HTML source: if we right-click on the page choose to view the source code, the IDs will be missing:

However, the IDs can be verified by pressing F12 in a Chrome browser and by navigating to the “elements” section:

It displays the dynamic HTML code as seen by the browser and we see that the id is correctly applied.

Step 4: Add and test Links to the Table of Contents

Now, since call headlines are equipped with unique IDs, we can make use of it. Within the forEach loop above, we add the following list item to the table of contents list:

(we need to use a screenshot, since this blog is saved on WordPress and WordPress refuses to save the embedded HTML code correctly)

We place it within the forEach loop:

  private getToc(content: any) {
     // create div for holding the content
     var contentdiv = document.createElement("div");
     contentdiv.innerHTML = content;

     // create an array of headlines:
     var myArrayOfHeadlineNodes = [].slice.call(contentdiv.querySelectorAll("h1, h2"));

...

     // will be appended to the node id to make sure it is unique on the page:
     var id_suffix = 0;

     // loop through the array of headlines
     myArrayOfHeadlineNodes.forEach(
       function(value, key, listObj) {

...

           // if headline has no id, add a unique id
           if ("" == value.id) {
               value.id = "id394752934579" + ++id_suffix;
           }
...
           
       }
     );

     // debugging:
     console.log(toc.innerHTML);

     // update the content with the changed contentdiv, which contains IDs for every headline
     //   note that we need to use the saniztizer.bypassSecurityTrustHtml function in order to tell angular
     //   not to remove the IDs, when used as [innerHtml] attribute in the HTML template
     this.content = this.sanitizer.bypassSecurityTrustHtml(contentdiv.innerHTML);

     return(toc.innerHTML);
  }

Step 5: Verify the Links

With the changes of the previous step, the page will contain links in the table of contents after being reloaded:

Moreover, the page will jump to the desired position, if one of the links is clicked. E.g. after clicking on the Step 1 link, we will see:

Excellent! Thump up!

This is, what we have aimed at in this short session.

Caveat A: Error Message ‘document is not defined’ (workaround given)

In the window, where npm run server is running, we see following error message, when we access the /blog URL:

ERROR ReferenceError: document is not defined
    at BlogView.exports.modules.490.BlogView.getToc (/localdir/oveits__ng-universal-demo/dist/0.server.js:59:35)
    at SafeSubscriber._next (/localdir/oveits__ng-universal-demo/dist/0.server.js:52:31)
    at SafeSubscriber.__tryOrUnsub (/localdir/oveits__ng-universal-demo/dist/server.js:601:16)
    at SafeSubscriber.next (/localdir/oveits__ng-universal-demo/dist/server.js:548:22)
    at Subscriber._next (/localdir/oveits__ng-universal-demo/dist/server.js:488:26)
    at Subscriber.next (/localdir/oveits__ng-universal-demo/dist/server.js:452:18)
    at MapSubscriber._next (/localdir/oveits__ng-universal-demo/dist/server.js:29934:26)
    at MapSubscriber.Subscriber.next (/localdir/oveits__ng-universal-demo/dist/server.js:452:18)
    at ZoneTask.onComplete [as callback] (/localdir/oveits__ng-universal-demo/dist/server.js:32771:30)
    at ZoneDelegate.invokeTask (/localdir/oveits__ng-universal-demo/dist/server.js:90301:31)

Workaround:

The reason for the problem is that node.js (i.e. the server side) does not understand some of the javascript code we have used in getToc function. A workaround is found on the Readme of Universal (“Universal Gotchas”):

The difference is shown the output of a git diff:

$ git diff 8ca27d5..3225b9a
--- a/src/app/+blog/blog.module.ts
+++ b/src/app/+blog/blog.module.ts
@@ -7,6 +7,9 @@ import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
 import { Pipe, PipeTransform } from '@angular/core';
 import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
+import { PLATFORM_ID } from '@angular/core';
+import { isPlatformBrowser, isPlatformServer } from '@angular/common';
+import { Inject } from '@angular/core';

 @Component({
   selector: 'blog-view',
@@ -18,7 +21,7 @@ export class BlogView implements OnInit {
   content: any = null;
   toc: any = null;

-  constructor(private http: Http, private sanitizer: DomSanitizer) {
+  constructor(private http: Http, private sanitizer: DomSanitizer, @Inject(PLATFORM_ID) private platformId: Object) {

   }

@@ -31,8 +34,11 @@ export class BlogView implements OnInit {
                 .map((res: Response) => res.json())
                  .subscribe(data => {
                        this.title = data.title;
-                       this.content = data.content;
-                        this.toc = this.getToc(this.content);
+                        this.content = data.content;
+                        if (isPlatformBrowser(this.platformId)) {
+                            // Client only code.
+                            this.toc = this.getToc(this.content);
+                        }
                         //console.log(data);
                         console.log("content = " + this.content.changingThisBreaksApplicationSecurity);
                 });

I call it “workaround” and not “resolution” because I would prefer the table of contents code to play well with the server. With this workaround, we just make sure that the code is not run on the server. This was, the table of contents is not shown in the HTML source.

Caveat B: ERROR TypeError: this.html.charCodeAt is not a function (resolved)

In the window, where npm run server is running, we see following error message, when we access the /blog URL:

ERROR TypeError: this.html.charCodeAt is not a function
    at Preprocessor.advance (/localdir/oveits__ng-universal-demo/dist/server.js:111990:24)
    at Tokenizer._consume (/localdir/oveits__ng-universal-demo/dist/server.js:22767:30)
    at Tokenizer.getNextToken (/localdir/oveits__ng-universal-demo/dist/server.js:22725:23)
    at Parser._runParsingLoop (/localdir/oveits__ng-universal-demo/dist/server.js:81548:36)
    at Parser.parseFragment (/localdir/oveits__ng-universal-demo/dist/server.js:81503:10)
    at Object.parseFragment (/localdir/oveits__ng-universal-demo/dist/server.js:35450:19)
    at Parse5DomAdapter.setInnerHTML (/localdir/oveits__ng-universal-demo/dist/server.js:33459:49)
    at Parse5DomAdapter.setProperty (/localdir/oveits__ng-universal-demo/dist/server.js:33100:18)
    at DefaultServerRenderer2.setProperty (/localdir/oveits__ng-universal-demo/dist/server.js:34618:109)
    at BaseAnimationRenderer.setProperty (/localdir/oveits__ng-universal-demo/dist/server.js:92084:23)

Resolution

I have found this issue on git, with some plunkr code that has helped me to sort this out: it seems like I need to define the content, title and toc with types as follows (instead of e.g. content: any = null):

 private title : SafeHtml|String = '';
 private toc : SafeHtml|String = '';
 private content : SafeHtml|String = '';

As a side effect, I had to exchange

this.content = this.sanitizer.bypassSecurityTrustHtml(data.content);

by the simpler expression

this.content = data.content;

and

console.log("content = " + this.content.changingThisBreaksApplicationSecurity);

by the simpler expression

console.log("content = " + this.content);

After those changes, the error messages disappeared.

The first commit, where this is implemented is 2adaa98  and can be reviewed with:

git clone https://github.com/oveits/ng-universal-demo
git checkout 2adaa98

Summary

In this short session, we have enriched the the automatic table of contents of the the previous blog post with links to the corresponding headline position. For that, we have

    • added IDs to all headlines
    • made sure the IDs are not stripped by Angular’s default security policy
    • added links to the table of contents
    • verified the results in Chrome’s debugger
    • tested the results

We only have used plain vanilla javascript functionality to add the IDs and links. Most probably, there exist more Angular’ish ways of performing the same (or better) result. We will explore this in future, most probably. However, it works well for my use case for now.

Download the Code

The code can be cloned via

# git clone https://github.com/oveits/ng-universal-demo
# cd ng-universal-demo
# git checkout 8ca27d5   # to be sure you are working with exact same that has been created in this blog

In addition to the features described, you will see that I have included a ScrollTo function from the bottom of the page to the top of the page.

View the Component Code

For your reference, see here the full code (changes highlighted in bold green) we have applied to the file

src/app/+blog/blog.module.ts

import {NgModule, Component, OnInit} from '@angular/core'
import {RouterModule} from '@angular/router'
import { Http, Response, Headers } from '@angular/http';
import 'rxjs/add/operator/map'
import { Observable } from 'rxjs/Observable';
import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
//import * as angular from "angular";

@Component({
  selector: 'blog-view',
  templateUrl: './blog.module.html'
})

export class BlogView implements OnInit {
  title: any = null;
  content: any = null;
  toc: any = null;

  constructor(private http: Http, private sanitizer: DomSanitizer) {

  }

  ngOnInit(){
    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(data => {
                        this.title = data.title;
                        this.content = data.content;
                        this.toc = this.getToc(this.content);
                        //console.log(data);
                        console.log("content = " + this.content.changingThisBreaksApplicationSecurity);
                });
  }

  private getToc(content: any) {
     // create div for holding the content
     var contentdiv = document.createElement("div");
     contentdiv.innerHTML = content;

     // create an array of headlines:
     var myArrayOfHeadlineNodes = [].slice.call(contentdiv.querySelectorAll("h1, h2"));

     // initialize table of contents (toc):
     var toc = document.createElement("ul");

     // initialize a pointer that points to toc root:
     var pointer = toc;

     // will be appended to the node id to make sure it is unique on the page:
     var id_suffix = 0;

     // loop through the array of headlines
     myArrayOfHeadlineNodes.forEach(
       function(value, key, listObj) {

           // if we have detected a top level headline ...
           if ( "H1" == value.tagName ) {
               // ... reset the pointer to top level:
               pointer = toc;
           }

           // if we are at top level and we have detected a headline level 2 ...
           if ( "H2" == value.tagName && pointer == toc ) {
               // ... create a nested unordered list within the current list item:
               pointer = pointer.appendChild(document.createElement("ul"));
           }

           // if headline has no id, add a unique id
           if ("" == value.id) {
               value.id = "id934752938475" + ++id_suffix;
           }

           // for each headline, create a list item with the corresponding HTML content:
           var li = pointer.appendChild(document.createElement("li"));
           
       }
     );

     // debugging:
     console.log(toc.innerHTML);

     // update the content with the changed contentdiv, which contains IDs for every headline
     //   note that we need to use the sanitizer.bypassSecurityTrustHtml function in order to tell angular
     //   not to remove the IDs, when used as [innerHtml] attribute in the HTML template
     this.content = this.sanitizer.bypassSecurityTrustHtml(contentdiv.innerHTML);

     return(toc.innerHTML);
  }
}

@NgModule({
  declarations: [BlogView],
  imports: [
    ScrollToModule.forRoot(),
    RouterModule.forChild([
      { path: '', component: BlogView, pathMatch: 'full'}
    ])
  ]
})

export class BlogModule {

}

Next Steps + Improvement Potential

Do we have improvement potential? Lots of them:

  • Make the Code SSR compatible:
    We are running our code on basis of universal, a server-side rendering solution for Angular. However, we have made use of functions that are only defined in the browser and are not compatible with the node.js server. This leads to the fact that the table of contents is not visible in the server-provided HTML source code. I would like to improve that. This caveat is not relevant for pure client-side rendered solutions, which, I guess, will be the majority of the Angular projects.
  • Be more flexible on the headline levels displayed
    Today, we assume that H1 headlines are present and only H1 and H2 levels are displayed (fixed). A better solution should be more flexible. There might be situations, where H2 or H3 is the top-level headline and we do not want to rewrite your javascript/typescript code for those situations. For that we might also evaluate existing modules like this one from 2014.
  • Replace normal links by a scrollTo function
    nice to have feature

For achieving the first and the third topic, it might be helpful to place the table of contents in its own component with own html template
and use ng-repeat functionality similar to the answer of this StackOverflow Q&A. Why is this interesting? I have tried to make use of Nicky Lenaers ngx-scroll-to plugin, since I wanted the browser to scroll down instead of jumping to the headlines. However, the scroll-to plugin is ignored, if I just add it within the getToc function as a string:

By applying the sanitizer functions to the toc, I have succeeded that the ng-scroll-to function is not stripped, when the toc is applied as innerHTML. However, the ng-scroll-to just did not do anything. I guess, the situation changes, if the ng-scroll-to code is placed in a html template of a component. Even the SSR compatibility might be achieved more easily, if the toc is encapsulated in its own component, as I hope.

 

 

0

Angular 4: Automatic Table of Contents


In this step by step tutorial, we will go through the process of creating a two-level automatic Table of Contents by adding Angular Typescript/javascript code.

We will perform following steps:

  • We will discuss alternative solutions.
  • We will start an Angular Docker Container.
  • We will download a demo application with server-side rendering and WordPress REST API integration.
  • Finally, we will enrich the Web page with a two-level table of contents using javascript methods.

In this blog post, we will we will concentrate on HTML and javascript, i.e. we will not care about styles of the table of contents. Moreover, the links to the headlines will be added later in part 2 of this little series.

Goal

The target of this exercise is to scan the content of a page for occurrences of headlines of level 1 and 2 (i.e. h1 and h2 elements)…

<h1>What is Angular?</h1>
<h1>Angular Hello World via Quickstart</h1>
<h2>Step 1: Start CentOS Container</h2>

… and to generate an unordered list of the headlines, e.g.

  • What is Angular?
  • Angular Hello World via Quickstart
    • Step 1: Start CentOS Container

In HTML, this is a unordered nested list like follows:

<ul> 
    <li>
        What is Angular?
    </li>  
    <li>
        Angular Hello World via Quickstart
        <ul>  
            <li>
                Step 1: Start CentOS Container
            </li>
            <li>
                ...
            </li>
        </ul>
    </li>
</ul>

We will scan the document using the querySelectorAll("h1, h2") and we will create the unordered list with javascript functions like appendChild(document.createElement("ul")) and.appendChild(document.createElement("li"))

Step 0: Make a Decision: Integrating an existing Solution or Starting from Scratch?

I have been looking for a table of contents generator for angular. I have found following material:

I guess any of those possibilities are fit to do, what we need, but since I am new to Angular, I have decided to create a TOC from scratch. Firstly, I will get more familiar with the Angular code, and secondly, I will have full control on the result. If you are familiar on how to integrate existing modules or directives, the links above might be a good alternative for you.

Step 1: Start the Base Project in a Docker Container

You are still reading? That means you have decided to create the table of contents from scratch without the help of any of the offered table of contents modules. Okay, let us start.

I have chosen to apply the needed changes to my Universal Base Project I have created in my previous blog post Angular 4: Boosting Performance through Server Side Rendering. For that we start an Angular Docker image and download the Universal Code from GIT:

(dockerhost)$ mkdir toc; cd toc
(dockerhost)$ docker run -it -p 8001:8000 -v $(pwd):/localdir oveits/angular_hello_world:centos bash
(container)# git clone https://github.com/oveits/ng-universal-demo
(container)# cd ng-universal-demo

My version of the ng-universal-demo has added a blog page that is automatically created from the contents of a WordPress blog post by downloading the information from WordPress’ REST API.

Let us install the dependencies and start the server. The npm run watch command will make sure that the transpilation from typescript to javascript is automatically re-done, as soon as a file change is detected:

(container)# npm i
(container)# npm run watch &
(container)# npm run server

Note: if you need help with setting up a Docker host on a Windows system, you may want to check out Step 0 of this blog post (search for the term “Install a Docker Host”). There, we describe how to run an Ubuntu based Docker host inside a Virtualbox VM.

Step 2: Generate a private getToc Function

In this step, we create a private function that will return a table of contents from the string content in the argument. For that, we create a private function getToc within the BologView class we had created in my previous blog post:

src/app/+blog/blog.module.ts

export class BlogView implements OnInit {
  ...
  private getToc(content: any) {
     // add code here ...
  }
  ...
}

Step 2.1: Generate a DIV element

I have learned that Angular is using typescript and typescript is a super-set of javascript. So, why not starting with normal typescript code? I have tried following valid (I hope!) javascript code.

var div = document.createElement("div");

Even though it seemed to work, I have seen following error messages in my Universal project:

ERROR TypeError: this.html.charCodeAt is not a function
...
ERROR ReferenceError: document is not defined

Even though the TOC I had created with this command was visible in the browser, it did not show up in the HTML source. Therefore, I guess, the code is valid in the Browser, but it is not valid on the server, so server-side rendering does not take place. Therefore, I have replaced the line by:

 (as a screenshot, because WordPress gets confused by the embedded HTML content).

Note: that with angular.element, we see more serious errors than with document.createElement, as you will see below. Before long, we will revert back to the original code with document.createElement, in order to avoid major problems with server side rendering.

Step 2.2: Read in the Content to the DIV

In my case, the content has been read from a REST API as a string with HTML code inside:

content = "In this hello world style tutorial, we will follow a step by step guide..."

It is easy to read in the HTML code into the div:

div.innerHTML = content;

Step 2.3: Read the Headlines from the Content

Now, we would like to read all headlines level 1 and 2 from it. This can be done with the querySelectorAll function:

var myArrayOfNodes = [].slice.call(div.querySelectorAll("h1, h2"));

I have cast it in an array of DOM nodes for easier manipulation.

Step 2.4: Create the Table of Contents

Now we create the table of contents:

var toc = document.createElement("ul");

Step 2.5: For each Headline, create a List Item in the correct Level

There might more elegant solutions to solve this, but we can make sure that we are at the top level of list items by defining a target pointer that is reset to the top level if a level 1 headline is detected. If a level 2 headline is detected, and we are still at the top level, we will create a nested unordered list (UL) within the current list item (LI). Here, we repeat the commands of Step 2.1 to 2.4 in order to get the full picture:

private getToc(content: any) {
 // create div

 // read content into div:
 div.innerHTML = content;

 // create an array of headlines:
initialize table of contents (toc) and select all level 1 and 2 headers, reading them into an array:
 var myArrayOfNodes = [].slice.call(div.querySelectorAll("h1, h2"));

 // 
 var toc = document.createElement("ul");
 var pointer = toc;
 var myArrayOfNodes = [].slice.call(div.querySelectorAll("h1, h2"));

 // loop through the array of headlines
 myArrayOfNodes.forEach(
     function(value, key, listObj) {
     console.log(value.tagName + ": " + value.innerHTML);

     // if we have detected a top level headline:
     if ( "H1" == value.tagName ) {
         // reset the pointer to top level:
         pointer = toc;
     }
     
     // if we are at top level and we have detected a headline level 2
     if ( "H2" == value.tagName && pointer == toc ) {
         // create a nested unordered list
         pointer = pointer.appendChild(document.createElement("ul"));
     }
 
     // for each headline, create a list item with the corresponding HTML content:
     var li = target.appendChild(document.createElement("li"));
     li.innerHTML = value.innerHTML;
     }
  
     // for debugging:
     console.log(toc.innerHTML);
 }

 return(
     toc.innerHTML
 );
}

Note that we will replace the line  by the line var div = document.createElement("div"); soon, since it behaves better with server side rendering. See below.

Finally, we return the unordered nested list as a string by using the innerHTML function. We also could return the toc as DOM, but I have decided to return the innerHTML since this is the same format we get the title and the content from WordPress’ REST API.

Step 3: Assign the Table of Contents to a Class Variable

Now, since we have defined a function that can create a table of contents from any HTML content, we need to make use of it. Remember from the last blog, that we had read the title and content from a blog into public class variables. We now add a variable named “toc” and assign the result of getToc(content) to it. The changes are marked in blue.

export class BlogView implements OnInit {
 title: any = null;
 content: any = null;
 toc: any = null;

 constructor(private http: Http) {
 }

 ngOnInit(){
    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(data => {
             this.title = data.title;
             this.content = data.content;
             this.toc = this.getToc(data.content);
         console.log(data);
     });
 }

The only new line (in blue) it the one, where we write the table of contents into a public variable named toc.

Step 4: Place the Table of Contents in the HTML Template

Last but not least, we want to make the table of contents visible by adding it to the HTML template.

src/app/+blog/blog.module.html

Here, we have added the second line. We now can see, why we have returned the table of contents as  String: this way we are able to handle the toc variable as if it was just another element returned from the WordPress REST API, all of which are HTML content in string format.

Step 5: Check the Results

Finally, it is time to open a Browser, point it to localhost:8001 (since we have chosen port 8001 in step 1 above) and check the results:

We can see that the unordered list shows up between title and content the way expected.

Excellent! Thump up!

But does it play well with server-side rendering? Let us check:

No, it does not. The whole HTML content is missing.

😦

I could partially remedy the problem by changing back the line

which is causing an error “window is not defined”

by

var div = document.createElement("div");

which is causing the error “ERROR TypeError: this.html.charCodeAt is not a function”.

However, the latter error is better in the sense that title and content are shown as HTML source code again:

The table of contents still does not show up as HTML source code, but title and content are back. However, the table of contents is visible in the browser, which is more important than showing it in the source code:

And this is as good as we can get for today. We will accept the …

Caveat

Table of contents is not shown in the HTML source code.

Workaround: in getToc, analyze the input HTML content string without converting it to a DOM object and create the output table of contents using string functions only. However, this approach is error-prone and tedious, so I have decided to live with the error messages and the fact that the table of contents does not show up as source HTML code.

Summary

Based on an example with server-side rendering and content retrieved via the WordPress REST API, we have performed following steps:

  • We have shown how to create a private getToc function that will create a table of contents from the web page.
  • We have shown how to analyze the document.
  • We have created a nested two-level table of contents from the list of headlines of the document.

The generic javascript functions we have used do not play well with node.js that is used in case of server-side rendering. However, the table of contents shows up in the browser so the solution will be fit for pure client-side rendering. Moreover, we have suggested a workaround that even will work in a situation with server-side rendering: create the table of contents as an explicit string containing HTML code.

Note: The resulting code can be cloned via

git clone https://github.com/oveits/ng-universal-demo; cd ng-universal-demo
git checkout 8b3948a8 # to be sure to be at the same status of the repo as described in the blog

Next

See Part 2: Adding Links to the Table of Contents items

2

Angular 4 Universal: Boosting Performance through Server Side Rendering


This time we will show, how to use server side rendering with Angular 4 (or Angular 2). Like in my previous blog post, we will consume a RESTful Web Service with Angular 4. However, the web page will be displayed almost immediately because of server side rendering, as opposed to the client side rendered situation described in my previous blog post about Angular consuming a REST API. There, we had observed load times of several seconds in situations with low bandwidth between Angular server and REST service.

Why Server Side Rendering?

In our last blog post, Consuming a RESTful Web Service with Angular 4,  we have created an Angular simple single page application that has displayed data from an external resource, the WordPress API. In case of limited bandwidth between client and WordPress API, the latency for some responses were several seconds (!). Because of this fact, the perceived performance of the application was quite poor.

Angular Universal offers a possibility to mitigate the problem: it offers a combination of server side rendering and client side rendering. When the client contacts its server, the complete HTML page is downloaded to the client and the client can display the content immediately. Then the client will perform the REST call and will replace the server-side rendered page by a client-side rendered page. In case of a low performance link to the RESTful interface, this can be perceived by a flickering of the page. This is not perfect, but it is much better than waiting for the page display for seconds.

Now let us begin with our step by step guide.

Phase 1: Run a Server Side Rendered Hello World Page

Step 1.0: Get a Docker Host

This time again, our application will need more than 750 MB RAM. Therefore, we cannot use my beloved Katacoda as our Docker playground, which is limited to this amount of DRAM. Instead, you need to get access to a Docker host with, >~ 2 GB RAM. A nice way of installing an Ubuntu Docker host via Vagrant is described here (search for the term “Install a Docker Host”).

Step 1.1 Run my CentOS Angular Image

On the Docker host, let us start my angular image like follows (the -v $(pwd):/localdir option is only needed, if you want to keep the project folder for later use)

(dockerhost)$ docker run -it -p 8000:8000 -v $(pwd):/localdir oveits/angular_hello_world:centos bash

Then, on the container, we perform following commands:

(container)# git clone https://github.com/FrozenPandaz/ng-universal-demo
(container)# cd ng-universal-demo
(container)# npm i
(container)# npm run watch &
[1] 37

> ng-universal-demo@1.0.0 watch /ng-universal-demo
> webpack --watch


Webpack is watching the files…

Hash: 068db451109474765aa6ca44939d0968e157c122
Version: webpack 2.6.1
Child
    Hash: 068db451109474765aa6
    Time: 15786ms
              Asset       Size  Chunks                    Chunk Names
        0.client.js    2.13 kB       0  [emitted]
          client.js     2.5 MB       1  [emitted]  [big]  main
    0.client.js.map    1.04 kB       0  [emitted]
      client.js.map    3.03 MB       1  [emitted]         main
         index.html  222 bytes          [emitted]
       [0] ./~/rxjs/Observable.js 11.4 kB {1} [built]
       [3] ./~/rxjs/util/root.js 885 bytes {1} [built]
       [7] ./~/@angular/platform-browser/@angular/platform-browser.es5.js 141 kB {1} [built]
       [9] (webpack)/buildin/global.js 509 bytes {1} [built]
      [19] ./~/rxjs/add/operator/map.js 187 bytes {1} [built]
      [39] ./~/@angular/platform-browser-dynamic/@angular/platform-browser-dynamic.es5.js 5.88 kB {1} [built]
      [40] ./src/app/browser-app.module.ts 1.4 kB {1} [built]
      [41] ./~/reflect-metadata/Reflect.js 48 kB {1} [built]
      [42] ./~/zone.js/dist/zone.js 96 kB {1} [built]
      [44] ./~/@angular/compiler/@angular/compiler.es5.js 1.02 MB {1} [built]
      [45] ./src/app/app.module.ts 1.61 kB {1} [built]
      [47] ./src/main.browser.ts 350 bytes {1} [built]
      [49] ./src/modules/transfer-state/browser-transfer-state.module.ts 1.35 kB {1} [built]
      [50] ./~/process/browser.js 5.42 kB {1} [built]
      [78] ./~/rxjs/util/toSubscriber.js 760 bytes {1} [built]
        + 66 hidden modules

    ERROR in /ng-universal-demo/node_modules/rxjs/Subject.d.ts (16,22): Class 'Subject' incorrectly extends base class 'Observable'.
      Types of property 'lift' are incompatible.
        Type '(operator: Operator<T, R>) => Observable' is not assignable to type '(operator: Operator<T, R>) => Observable'.
          Type 'Observable' is not assignable to type 'Observable'.
            Type 'T' is not assignable to type 'R'.
    Child html-webpack-plugin for "index.html":
           [0] ./~/html-webpack-plugin/lib/loader.js!./src/index.html 193 bytes {0} [built]
Child
    Hash: ca44939d0968e157c122
    Time: 21298ms
              Asset     Size  Chunks                    Chunk Names
        0.server.js  2.14 kB       0  [emitted]
          server.js  4.23 MB       1  [emitted]  [big]  main
    0.server.js.map  1.04 kB       0  [emitted]
      server.js.map  5.18 MB       1  [emitted]         main
       [4] ./~/@angular/core/@angular/core.es5.js 489 kB {1} [built]
     [145] ./src/api/app.ts 222 bytes {1} [built]
     [146] ./src/app/server-app.module.ts 2.2 kB {1} [built]
     [147] ./src/routes.ts 80 bytes {1} [built]
     [148] ./~/@nguniversal/express-engine/index.js 196 bytes {1} [built]
     [149] ./~/express/index.js 224 bytes {1} [built]
     [150] ./~/reflect-metadata/Reflect.js 48 kB {1} [built]
     [151] ./~/rxjs/Rx.js 9.65 kB {1} [built]
     [152] ./~/zone.js/dist/zone-node.js 71.1 kB {1} [built]
     [158] ./src/main.server.ts 1.22 kB {1} [built]
     [245] ./~/rxjs/add/operator/bufferCount.js 235 bytes {1} [built]
     [336] ./~/rxjs/add/operator/windowTime.js 229 bytes {1} [built]
     [337] ./~/rxjs/add/operator/windowToggle.js 241 bytes {1} [built]
     [338] ./~/rxjs/add/operator/windowWhen.js 229 bytes {1} [built]
     [339] ./~/rxjs/add/operator/withLatestFrom.js 253 bytes {1} [built]
        + 475 hidden modules

    WARNING in ./~/express/lib/view.js
    80:29-41 Critical dependency: the request of a dependency is an expression

    ERROR in /ng-universal-demo/node_modules/rxjs/Subject.d.ts (16,22): Class 'Subject' incorrectly extends base class 'Observable'.
      Types of property 'lift' are incompatible.
        Type '(operator: Operator<T, R>) => Observable' is not assignable to type '(operator: Operator<T, R>) => Observable'.
          Type 'Observable' is not assignable to type 'Observable'.
            Type 'T' is not assignable to type 'R'.

    ERROR in /ng-universal-demo/node_modules/rxjs/observable/dom/WebSocketSubject.d.ts (24,22): Class 'WebSocketSubject' incorrectly extends base class 'AnonymousSubject'.
      Types of property 'lift' are incompatible.
        Type '(operator: Operator<T, R>) => WebSocketSubject' is not assignable to type '(operator: Operator<T, R>) => Observable'.
          Type 'WebSocketSubject' is not assignable to type 'Observable'.
            Types of property 'operator' are incompatible.
              Type 'Operator<any, R>' is not assignable to type 'Operator<any, T>'.
                Type 'R' is not assignable to type 'T'.

Let us ignore the error in red for now.

(container)# npm run server
[2] 60

> ng-universal-demo@1.0.0 server /ng-universal-demo
> nodemon dist/server.js

[nodemon] 1.11.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: dist/*server.js src/index.html
[nodemon] starting `node dist/server.js`
Listening at http://localhost:8000
GET: /: 187.390ms
GET: /data: 3.213ms

or if you want to avoid the npm run watch error described as issue on git:angular/angular, you either can fix the typescript version in package.js: "typescript": "2.3.4". Alternatively you can replace the previous two commands by npm run start (the npm run server command, which needs to be restarted often below, is quicker though):

(container)# npm run start
> ng-universal-demo@1.0.0 start /localdir/FrozenPandaz__ng-universal-demo
> npm run build && npm run server


> ng-universal-demo@1.0.0 prebuild /localdir/FrozenPandaz__ng-universal-demo
> npm run clean


> ng-universal-demo@1.0.0 clean /localdir/FrozenPandaz__ng-universal-demo
> rimraf dist


> ng-universal-demo@1.0.0 build /localdir/FrozenPandaz__ng-universal-demo
> webpack

Hash: a746c6e416ab32c2fe97cac872fdf2e493c7e402
Version: webpack 2.6.1
Child
    Hash: a746c6e416ab32c2fe97
    Time: 18765ms
              Asset       Size  Chunks                    Chunk Names
        0.client.js    2.07 kB       0  [emitted]
          client.js     2.5 MB       1  [emitted]  [big]  main
    0.client.js.map    1.07 kB       0  [emitted]
      client.js.map    3.03 MB       1  [emitted]         main
         index.html  222 bytes          [emitted]
       [0] ./~/rxjs/Observable.js 11.4 kB {1} [built]
       [3] ./~/rxjs/util/root.js 885 bytes {1} [built]
       [7] ./~/@angular/platform-browser/@angular/platform-browser.es5.js 141 kB {1} [built]
       [9] (webpack)/buildin/global.js 509 bytes {1} [built]
      [19] ./~/rxjs/add/operator/map.js 187 bytes {1} [built]
      [39] ./~/@angular/platform-browser-dynamic/@angular/platform-browser-dynamic.es5.js 5.88 kB {1} [built]
      [40] ./src/app/browser-app.module.ts 1.35 kB {1} [built]
      [41] ./~/reflect-metadata/Reflect.js 48 kB {1} [built]
      [42] ./~/zone.js/dist/zone.js 96 kB {1} [built]
      [44] ./~/@angular/compiler/@angular/compiler.es5.js 1.02 MB {1} [built]
      [45] ./src/app/app.module.ts 1.55 kB {1} [built]
      [47] ./src/main.browser.ts 350 bytes {1} [built]
      [49] ./src/modules/transfer-state/browser-transfer-state.module.ts 1.31 kB {1} [built]
      [50] ./~/process/browser.js 5.42 kB {1} [built]
      [78] ./~/rxjs/util/toSubscriber.js 760 bytes {1} [built]
        + 66 hidden modules
    Child html-webpack-plugin for "index.html":
           [0] ./~/html-webpack-plugin/lib/loader.js!./src/index.html 193 bytes {0} [built]
Child
    Hash: cac872fdf2e493c7e402
    Time: 24164ms
              Asset     Size  Chunks                    Chunk Names
        0.server.js  2.07 kB       0  [emitted]
          server.js  4.23 MB       1  [emitted]  [big]  main
    0.server.js.map  1.07 kB       0  [emitted]
      server.js.map  5.18 MB       1  [emitted]         main
       [4] ./~/@angular/core/@angular/core.es5.js 489 kB {1} [built]
     [145] ./src/api/app.ts 222 bytes {1} [built]
     [146] ./src/app/server-app.module.ts 2.11 kB {1} [built]
     [147] ./src/routes.ts 80 bytes {1} [built]
     [148] ./~/@nguniversal/express-engine/index.js 196 bytes {1} [built]
     [149] ./~/express/index.js 224 bytes {1} [built]
     [150] ./~/reflect-metadata/Reflect.js 48 kB {1} [built]
     [151] ./~/rxjs/Rx.js 9.65 kB {1} [built]
     [152] ./~/zone.js/dist/zone-node.js 71.1 kB {1} [built]
     [158] ./src/main.server.ts 1.22 kB {1} [built]
     [245] ./~/rxjs/add/operator/bufferCount.js 235 bytes {1} [built]
     [336] ./~/rxjs/add/operator/windowTime.js 229 bytes {1} [built]
     [337] ./~/rxjs/add/operator/windowToggle.js 241 bytes {1} [built]
     [338] ./~/rxjs/add/operator/windowWhen.js 229 bytes {1} [built]
     [339] ./~/rxjs/add/operator/withLatestFrom.js 253 bytes {1} [built]
        + 475 hidden modules

    WARNING in ./~/express/lib/view.js
    80:29-41 Critical dependency: the request of a dependency is an expression

> ng-universal-demo@1.0.0 server /localdir/FrozenPandaz__ng-universal-demo
> nodemon dist/server.js

[nodemon] 1.11.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: dist/*server.js src/index.html
[nodemon] starting `node dist/server.js`
Listening at http://localhost:8000

Now we connect to port 8000 of the Docker host, I have mapped the internal port 8000 to:

When looking at the source code, we can see the server-side rendered HTML Code:

The text “Universal Demo” and “Hello World” are visible in the source.

Excellent! Thump up!

This is exactly, what we wanted to see: a web page with the full HTML content, not just the “Loading…” directive that you usually see in Angular Index files.

Phase 2: Create a functional new Link in the main Page

Step 2.1 Create a new Link on the Home Page

With following little change in blue, we will add a new link to the Hello World page:

src/app/app.component.ts

import { Component, OnInit } from '@angular/core'
import { TransferState } from '../modules/transfer-state/transfer-state';

@Component({
  selector: 'demo-app',
  template: `
    <h1>Universal Demo</h1>
    <a routerLink="/">Home</a>
    <a routerLink="/lazy">Lazy</a>
    <a routerLink="/blog">Blog</a>
    <router-outlet></router-outlet>
  `,
  styles: [
    `h1 {
      color: green;
    }`
  ]
})
export class AppComponent implements OnInit {
  constructor(private cache: TransferState) {}
  ngOnInit() {
    this.cache.set('cached', true);
  }
}

From the steps above, npm run watch is still running in the background.

It seems that npm run server needs to be stopped are restarted manually. Since we have started it in the foreground above, a <Ctrl>-C and re-issuing the command is sufficient:

(container)# <Ctrl>-C
(container)# npm run server

After that, the change should be seen immediately in the browser (try pressing F5 to refresh, if this is not the case):

You will notice, though, that the Blog link is not yet functional. When you press F12, choose the “console” tab in the Browser and reload the page, we will see, what is missing:

ERROR Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'blog'

We still need to define the route.

Step 2.2: Create a Route from /blog to a Module

For creating a route, we need to add the /blog route to following file:

src/routes.ts

export const ROUTES: string[] = [
  '/',
  '/lazy',
  '/blog'
];

As we can see in the browser network debugging (F12), the error message does not change:

ERROR Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'blog'

To turn this around, we need a second change: we need to add a link from ‘blog’ to a module. For now, let us point the /blog link to the same module as the /lazy link:

src/app/app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { APP_BASE_HREF, CommonModule } from '@angular/common';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { HomeView } from './home/home-view.component';
import { TransferHttpModule } from '../modules/transfer-http/transfer-http.module';


@NgModule({
  imports: [
    CommonModule,
    HttpModule,
    TransferHttpModule,
    RouterModule.forRoot([
      { path: '', component: HomeView, pathMatch: 'full'},
      { path: 'lazy', loadChildren: './+lazy/lazy.module#LazyModule'},
      { path: 'blog', loadChildren: './+lazy/lazy.module#LazyModule'}
    ])
  ],
  declarations: [ AppComponent, HomeView ],
  exports: [ AppComponent ]
})
export class AppModule {}

Now the /blog link is functional and is pointing to the lazy module, showing “i’m lazy”, when clicking on the Blog link.

Now let us create our own module that is pointing to “i’m a blog”

Step 2.3: Create your own Blog Module

Above, we have re-used an existing “LazyModule”. Now, let us create our own module by copying and changing LazyModule:

mkdir src/app/+blog
cp src/app/+lazy/lazy.module.ts src/app/+blog/blog.module.ts

We replace ‘lazy’ by ‘blog’ and ‘Lazy’ by ‘Blog’ in place:

sed -r -i "s/lazy/blog/g" src/app/+blog/blog.module.ts
sed -r -i "s/i'm blog/i'm a blog/g" src/app/+blog/blog.module.ts
sed -r -i "s/Lazy/Blog/g" src/app/+blog/blog.module.ts

After that, the content of blog.module.ts looks like follows:

Now we need to change the route to point to the new BlogModule:

sed -r -i '/blog/s/lazy/blog/g; /blog/s/Lazy/Blog/' src/app/app.module.ts

after that, the file content looks like follows:

src/app/app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { APP_BASE_HREF, CommonModule } from '@angular/common';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { HomeView } from './home/home-view.component';
import { TransferHttpModule } from '../modules/transfer-http/transfer-http.module';


@NgModule({
  imports: [
    CommonModule,
    HttpModule,
    TransferHttpModule,
    RouterModule.forRoot([
      { path: '', component: HomeView, pathMatch: 'full'},
      { path: 'lazy', loadChildren: './+lazy/lazy.module#LazyModule'},
      { path: 'blog', loadChildren: './+blog/blog.module#BlogModule'}
    ])
  ],
  declarations: [ AppComponent, HomeView ],
  exports: [ AppComponent ]
})
export class AppModule {}

After restarting the server and reloading the Browser, the link to ‘Blog’ leads to following output:

In the source view, we also can confirm that this is a server-rendered page:

This is exactly, what we wanted to see: a web page with the full HTML content, not just the “Loading…” directive that you usually see in Angular Index files. With that, we can see, that this a server rendered page.

Excellent! Thump up!

Step 2.4 Add Includes to Browser, Server and Server AOT

To be honest, I am a newbie to Angular and I do not exactly know the function of following three includes. However, I have found them by searching recursively for occurrences of the term “lazy”. Those three includes seem to be needed, although the server side rendering seems to look fine without as well.

This one might be needed for client side rendering within webpack:

src/tsconfig.browser.json

{
  "extends": "../tsconfig.json",
  "angularCompilerOptions": {
    "entryModule": "./app/browser-app.module#BrowserAppModule"
  },
  "include": [
    "./main.browser.ts",
    "./app/+lazy/lazy.module.ts",
    "./app/+blog/blog.module.ts"
  ]
}

AOT stands for “Ahead of Time” and often refers to the compile time. Since we are not compiling, but we are “transpiling” in case of Angular, I guess, the next file will control the server-side pre-tranpiled pages:

src/tsconfig.server.aot.json

{
  "extends": "./tsconfig.server.json",
  "angularCompilerOptions": {
    "genDir": "ngfactory",
    "entryModule": "./app/server-app.module#ServerAppModule"
  },
  "include": [
    "main.server.aot.ts",
    "./app/+lazy/lazy.module.ts",
    "./app/+blog/blog.module.ts",
    "./app/server-app.module.ts"
  ]
}

The next one is a server configuration file for non AOT pages?

src/tsconfig.server.json

{
  "extends": "../tsconfig.json",
  "angularCompilerOptions": {
    "entryModule": "./app/server-app.module#ServerAppModule"
  },
  "include": [
    "main.server.ts",
    "./app/+lazy/lazy.module.ts",
    "./app/+blog/blog.module.ts"
  ]
}

Phase 3: Inserting a WordPress Blog POST via RESTful API

In this phase, we will insert a single WordPress Blog Post via a RESTful API of WordPress, as we had done in my previous blog post “Consuming a RESTful Web Service with Angular 4“. However, this time, the page will be served server-side rendered, which helps for a much better user experience (quicker load time). Especially, the performance is improved substantially, if the WordPress REST API is reachable via a low-bandwidth connection only.

Step 3.1: Change your Blog Module to perform HTTP Requests

In order to perform HTTP requests, we need to adapt the file src/app/+blog/blog.module.ts file, so it performs the same function as did the file src/app/app.component.ts in my previous blog post about Angular consuming HTTP:

  • Like last time, we had to import Http, Response, Headers as well as map as well as Observable. We add Oninit as well, this time.
  • I have replaced the inline template by a templateUrl file. This also helps me to display the content of my Blog Module correctly. However, this will lead to typescript errors as long as the template is not created. We will do this soon.
  • Different from last time, we explicitly have defined private variables title and content. The reason we are not using data.title and data.content in the HTML template is, that the data is null as long as we are waiting for the HTML response and a title and content of null does not exist. When debugging the browser, errors are visible. With the private variable title and content, we do not create such errors.
  • Like last time, we need to define a private variable (_http in our case)with type Http as argument of the constructor
  • Different from last time, we have introduced an OnInit function, which is calling the getMyBlog() function, instead of calling this function in the constructor. However, both possibilities work fine.
  • The getMyBlog() function looks similar to last time. The only difference is, that we set the title and content explicitly.

src/app/+blog/blog.module.ts

import {NgModule, Component, OnInit} from '@angular/core'
import {RouterModule} from '@angular/router'
import { Http, Response, Headers } from '@angular/http';
import 'rxjs/add/operator/map'
import { Observable } from 'rxjs/Observable';


@Component({
  selector: 'blog-view',
  templateUrl: './blog.module.html'
})

export class BlogView implements OnInit {
  data: any = null;
  title: any = null;
  content: any = null;
  public subs: Observable<string>;

  constructor(private _http: Http) {
  }

  ngOnInit(){
    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(data => {
                        this.data = data;
                        this.title = this.data.title;
                        this.content = this.data.content;
                        console.log(this.data);
                });
  }

}

@NgModule({
  declarations: [BlogView],
  imports: [
    RouterModule.forChild([
      { path: '', component: BlogView, pathMatch: 'full'}
    ])
  ]
})
export class BlogModule {

}

The function getMyBlog() is retrieving the data of a blog post from the WP.COM Rest API v1.1, similar to what we also have done on the previous blog post Consuming a RESTful Web Service with Angular 4. This time we are writing the title and content in the corresponding public variables, which can be used in the HTML template.

Step 3.2 Create Blog HTML Template

If your npm run watch command is still active, you will notice following error in the console, where the command is running:

    ERROR in ./src/app/+blog/blog.module.ts
    Module not found: Error: Can't resolve './blog.module.html' in '/ng-universal-demo/src/app/+blog'
     @ ./src/app/+blog/blog.module.ts 38:18-47
     @ ./src lazy
     @ ./~/@angular/core/@angular/core.es5.js
     @ ./src/app/browser-app.module.ts
     @ ./src/main.browser.ts

The error will disappear, if we create the following file:

src/app/+blog/blog.module.html

The blog.module.ts file is pointing to a HTML templateUrl on ./blog.module.html with following content (as picture, since WordPress is confused about the HTML code, even if set in <pre>…</pre> tags):

Now the error is disappeared. If we restart the npm run server and the Browser content (e.g. press F5), we will see following content:

And different from last time, we not only see “Loading…” in the source of the page, but we will see the full HTML content:

To be honest, we see the content more often than we need: we see it as “innerHtml”, but we also see it explicitly as data within the tags. Large pages will have doubled size. Okay, seeing it twice is better than seeing it no time at all. Let us call it not perfect, but still …

Excellent! Thump up!

Summary

In this blog post, we have performed following tasks:

  • Phase 1: Run a Server Side Rendered Hello World Page
    • Here we could show that the server provides the browser with the full HTML content
  • Phase 2: Create a functional new Link in the main Page
    • Those steps are about Link creation and routing to a module
  • Phase 3: Inserting a WordPress Blog POST via RESTful API
    • In those steps we have shown how to create a component that retrieves content from a REST API and how to display the information in a browser

We could see, that the user is presented with the page content much more quickly than was the case in a client rendered solution of my previous blog. Especially, if there is a low bandwidth connection between Angular server and REST service, the user perception is improved a lot by server side rendering: the content is displayed almost immediately as opposed of the several second loading time we had experienced in case of client side rendering.

Appendix: Why using innerHtml?

Let us first demonstrate, what happens, if we use following template: The HTML template is making use of the variables “title” and “content” we have defined in the blog.module.ts before. We need to make use of this innerHtml trick in order to display the HTML-based content correctly. If we would use “{{content}}” instead, we will see escaped HTML.

This is not, what we want. If we change the template by following content:

or better (see this StackOverflow Q&A that states “Sanitized content can’t be bound using prop="{{sanitizedContent}}" because {{}} stringyfies the value before it is assigned which breaks sanitization.”; we do not yet sanitize here, but we might do so in the future):

With this, we will get, what we want:

Appendix: Webpack Problem

Error

Webpack has been initialised using a configuration object that does not match the API schema.

How to reproduce

docker run -it -p 8081:8000 -v $(pwd):/localdir oveits/angular_hello_world:centos bash
cd /localdir
git clone https://github.com/robwormald/ng-universal-demo
cd ng-universal-demo
npm i
$ npm start
> ng-universal-demo@1.0.0 start /localdir/ng-universal-demo
> npm run build && npm run server


> ng-universal-demo@1.0.0 build /localdir/ng-universal-demo
> webpack -p

Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
 - configuration.output.path: The provided value "dist" is not an absolute path!

Workaround

Clone the fork https://github.com/FrozenPandaz/ng-universal-demo instead.

Further Reading

0

Vagrant on CentOS 7 – Setting up Test Environments the easy Way


After stumbling upon several guides still describing a Vagrant installation via a RubyGem – which is no longer supported – the following article was created and will provide a quick setup guide on how to setup Vagrant on CentOS 7. All commands used in this guide are executed having root permissions.

Setting up VirtualBox as your Vagrant Provider

Since Vagrant is a utility to manage the lifecycle of virtual machines but doesn’t provide them it relies on providers. As described here Vagrant supports different providers by default such as  VirtualBoxHyper-V, and Docker. Due to the wide availability of VirtualBox it will be used in this guide.

In order to install VirtualBox we first need to add the VirtualBox repository:

cd /etc/yum.repos.d
yum install -y wget
wget http://download.virtualbox.org/virtualbox/rpm/rhel/virtualbox.repo

Because the installation of VirtualBox will require building kernel modules DKMS – Dynamic Kernel Module Support – from the EPEL repository will be installed first:

yum -y install epel-release
yum -y install dkms

In order to find the latest available VirtualBox version execute:

yum provides VirtualBox

and install via:

yum -y install VirtualBox-5.1-5.1.22_115126_el7-1.x86_64

Installing Vagrant

The latest Vagrant packages are available here. With a simple:

yum install https://releases.hashicorp.com/vagrant/1.9.6/vagrant_1.9.6_x86_64.rpm

the installation is completed in seconds.

Setting up your project environment

Vagrant projects are managed via Vagrantfiles. In order to start a new project create a dedicated folder and execute Vagrant init which will automatically create a new Vagrantfile:

mkdir Test_Project
cd Test_Project/
vagrant init

Now that we have our environment set up it is time to add a new base image. A variety of images – also called boxes – are available at https://app.vagrantup.com. We will add the latest Ubuntu Image to our boxes with:

vagrant box add ubuntu/xenial64

After the box was added locally we need to change the Vagrantfile which got created before. By changing the parameter config.vm.box from base to ubuntu/xenial64 Vagrant is configured to run the box we added previously.

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
end

Starting your  machine

Now that everything is set up the new machine is started with:

vagrant up

and is accessible by ssh executing:

vagrant ssh

The machine can be stopped with:

vagrant halt

Note: If you can not connect via ssh because you are asked for a password it might be related to: https://bugs.launchpad.net/cloud-images/+bug/1569237. You can switch to ubuntu/trusty64 by deleting your current Vagrantfile:

rm Vagrantfile

adding the ubuntu/trusty64 box:

vagrant box add ubuntu/trusty64

and create a new Vagrantfile containing a proper config.vm.box parameter:

vagrant init ubuntu/trusty64
0

Happiness Research and Agility- Two Sides of the Same Coin


We are all experts in the field of agility, at least to some point. We know that agility is first and foremost a mindset change. It changes our view on how we develop software or build products. It conflicts with the ways traditional organizations are used to work. Especially Scrum, which comes along without any hierarchy. There are only three roles, none is superior to the other. Introducing such a framework in a strong, long-established hierarchical organization inevitably leads to clashes. Managers need to rethink their role and duties. They must become something called a Servant Leader. Companies need to rethink their organizational structures. They need to find a balance between hierarchy and network structures. All this has led to a new understanding of management that’s nowadays known under the name Management 3.0.

But agility is also spreading over. It’s no longer related only to IT departments or organizations. Take eduScrum as an example where Scrum is used in schools and universities. Students learn their subjects using Scrum as methodology. Another example is Wikispeed, a group of people that produces cars using the Scrum framework. And even in military, agility is used. David Marquet was assigned the command of a nuclear-powered submarine, but was trained for a different submarine. What he found there was poor morale, poor performance, and the worst retention rate in the fleet. He then introduced agile principles and practices and skyrocketed from worst to first in the fleet.

Summing up, agility embraces a new understanding of management, challenges existing hierarchical organizational structures and finds its way into non-IT organizations. But what has all this to do with Happiness Research?

If one asks humans, what is the one thing that is closest to their heart, what they finally expect from live, happiness and content come first. Exactly this is where Happiness Research enters the scene. Happiness Research investigates what it is that makes people happy and content. To clarify, when we talk about happiness we mean the subjective well-being of people. Although Happiness Research has been part of the discussions amongst the economists since the 1970th, it just became an official science around the turn of the millennium. It is an interdisciplinary science covering philosophy, psychology, sociology, economics, and neuroscience. Especially the breakthrough in neuroscience and related medical devices, that enabled scientists to measure the brain waves, boosted the spreading of the science Happiness Research. One of the pioneers of Happiness Research is Lord Richard Layard. His studies laid the ground for the science and many other psychologists and sociologists jumping on that train of well-being. So now, what is it all about?

Let’s look at two figures, the real income per head and the percentage of people very happy. Research has revealed that in the US the real income per head has steadily increased since 1945. In contrast, the percentage of people that are very happy with their live has been a straight line. This trend is not special for the US, instead it was found likewise in all industrial countries. This means that even though people were getting richer they did not become any happier. Money alone doesn’t make people happy! But why? Why don’t people get happier even though they become wealthier?

Let’s consider three different research results from Happiness Research. First, studies all over the world have shown that the wealthier one is, the less is the impact of additional money on one’s happiness. Up to an income of 10.000 US-$ GDP per head per year there is a strong correlation between an increase in income and an increase in happiness. This is mostly due to the satisfaction of one’s existential basic needs like food, living, clothing, security, sex, and education. This correlates with the two bottom levels of Maslow’s Hierarchy of Needs – Physiological and Safety needs. Between 10.000 US-$ and 20.000 US-$ GDP per head per year the correlation is still there but getting weaker. Above 20.000 US-$ GDP per head per year the correlation nearly no longer exists. So, this means: One secret of happiness is to distribute income equally!

Now imagine you get an increase in salary. Well, this definitely makes you happy. But for how long? After a while the more money gets integrated into your daily live and the good feelings have diminished. To become happy again you need another increase in salary. The same is true for books. When I receive a book that I bought I get a feeling of overwhelming contentedness. But a few days later these happy feelings disappear. So, I buy more books to become happy again. And then I need even more, and more, and more. Meanwhile one needs a guide when walking through my library because all the bought but unread books are lying around the floor. This behavior is also true for cars, clothes, postcards, shoes, … every material stuff. The act that someone needs always more and more to satisfy his happiness, like a drug-dependent junkie, is called hedonistic treadmill. So, this means: Another secret of happiness is to choose positive things one does not get used to!

Let’s look at two studies Richard Layard and other researchers have performed.

  • In a first study, they asked students to answer the following question.
    Imagine you could choose in which of the two following worlds you want to live.

    1. In the first world, you earn 50.000 Euro per year, while the average earning of your fellow men is 25.000 Euro per year.
    2. In the second world, you earn 100.000 Euro per year, while the average earning of your fellow men is 250.000 Euro per year.

Question: Imagine, the prices in both worlds are the same. Which world would you choose?

The findings show that the vast majority of students had chosen the first world A. This means that people rather abstain from earning more money in favor of looking better than their fellow men.

  • In a second study, they asked students to answer the following question.
    Imagine you could choose in which of the two following worlds you want to live.

    1. In the first world, you have two weeks’ vacation per year, while your fellow men have only one week vacation per year.
    2. In the second world, you have four weeks’ vacation per year, while your fellow men have eight weeks’ vacation per year.

Question: Which world would you choose?

The findings show that the vast majority of students had chosen the second world B. This means that people want to have as much leisure time as possible, despite that their fellow men have much more leisure time.

Both studies reveal that there is a difference in people’s behavior when comparing money, or material things, on the one side, and leisure time, or immaterial things, on the other side. This social envy regarding the material or monetary side is another reason why people are unhappy with their lives. So, this means: Another secret of happiness is to not orientate oneself with people who are more successful than oneself!

Let’s summarize the three secrets of happiness.

  1. Income should be distributed equally.
  2. Choose positive things you do not get used to.
  3. Do not compare yourself with other people who are more successful.

But what does this mean? What can we draw out of these findings? What has all this to do with agility? Let’s have a look at how these findings relate to people and organizations.

  1. Distributing income equally is synonym to the declining worth money offers for employees. Especially creative workers can’t be motivated with money. For them other things are more important. Their work needs to be meaningful, they are looking for a purpose in their daily work, they value freedom of choice, self-organization, and to carry their life in their own hand. Empowerment gets more and more important. New motivational methods are indispensable. All those measures are expressions of the low value money plays and that money should better be distributed equally.
  2. Choosing positive things, one does not get used to, is a concept that is mirrored in the importance of social contacts in work environments. People prefer having good relationships with their teammates, instead of being compensated with money. People like to work in teams, have fun, and share their knowledge and experience. Team coherence is more important than monetary compensation. In addition, people are looking for meaningful and varied tasks. All things that one can’t get used to, even after month of existence.
  3. Do not compare yourself with other people who are more successful than oneself, is expressed in different ways. The ones who do not compare themselves with others turn towards their own needs. They focus on their personal growth and search for self-actualization, they like pair-working and sharing experience. The team takes center stage. Everything is secondary compared to one’s own life and happiness and the team performance.

Creative workers cannot be motived with money, thus new motivational approaches are required. Furthermore, more and more research studies from fields like neurobiology, psychology, and economics prove, that content and happy employees lead to better business performances. Happy employees accomplish more than unhappy ones in the long run. They come to work more regularly, are less often ill, are more successful, more engaged, more innovative, much more loyal, and they attract alike people. Happy employees are the best organizations can wish for. Organizations on the other hand become more productive, more effective, more efficient, more resilient, respond with faster reactions, and are much better economically viable. Taking all this together we can conclude that people need to be in the center of organizations. Organizations must focus on their employees’ needs. This requires a totally new understanding of management that changes the perception of roles and organizational structures.

OK, so let’s put the things together. As we all know from experience, agility changes organizations, it changes the mindset about what management means and hence the role of managers and organizational structures. This in turn changes people and influences our society. People strive for a happy, motivating and engaging workplace. On the other hand, we now have seen that Happiness Research revealed that the one thing that is closest to people’s heart is a happy and content live. And this not only holds true for their private lives but spreads into the workplace. People living to the core findings of Happiness Research exhibit behavior and expect work environments that require a completely new understanding of management, roles and organizational structures. Happiness Research influences the society and therefore people and organizations. Both, Happiness Research and Agility reinforce each other. They are two sides of the same coin.