0

Behavior-Driven Angular – part 1: Consuming a RESTful Web Service with Angular 4


In this step-by-step tutorial, we will follow a behavior-driven development approach to create an Angular 4 application from Angular CLI. The hello-world-like application will consume the WordPress REST API and it will display a blog post title. We will create and run end-to-end test scripts that simulate the customer behavior on a Chrome browser within a Protractor headless Docker container.

As a side feature of this tutorial, we will demonstrate basic Git handling: we will learn how to create a GIT Repository, create a feature branch, commit code changes, and merge the tested and fully functional feature branch into the main development branch.

Check out this book on Amazon: Angular Test-Driven Development

Introduction

My post Consuming a RESTful Web Service with Angular 4 has grown much more popular than expected. Thanks a lot for your views! The September ist still not finished, and the article has achieved more than 3000 views in its fourth month. I hope the trend will keep on:

😉

So, why would I want to rework a blog post that seemingly plays a chord in the developer’s community? The reasons I have started to rework the example are:

  • I have come to a point, where I had the need to refactor the code. However, I do not like refactoring before I do not have a good coverage of end-to-end tests. This was fixed easily in my previous blog post Angular end-to-end Testing.
  • The next topic was not so easy to be resolved: I had created a working example, but when I have created a GIT repository from it, Angular CLI had a problem with new clones of that code. An Angular problem I could not resolve easily and it looked like I had to start from scratch. This is, what I am doing now, committing many snapshots to GIT. If I so, why not explaining to my audience, what I am doing and why? This way, the current post has become an example that demonstrates basic GIT handling.

This blog post will fix those two issues, I deem.

Even if I am tempted to automate many of the development process steps, we will keep it simple without the usage of DevOps tools like Jenkins with BitBucket, Sonar, BrowserStack, JMeter Integration and Docker data center integration you would find in real-world agile projects. Some of such topics can be explored in more detail on my other blog posts on Jenkins (explore the “Jenkins Tutorial” drop-down menu of my blog).

Why behavior driven development?

I have made a very good experience with behavior driven development (BDD), or “test first” development. Some years ago, I have applied this principle on a ProvisioningEngine I had developed based on Ruby on Rails and java (Apache Camel). The advantages of BDD I see are:

  • better customer view: if you follow the behavior driven principle, your first thought is, how the web pages look like and how the web pages behave with respect to customer actions — in detail. This helps me to always start with the customer view in mind.
  • higher motivation: as a developer, I find it rewarding to start with test development with “red” test cases that become green over time
  • higher quality: I often challenge myself to optimize my code (e.g. make it DRYer of more versatile). In this process, I do not want to sacrifice and previous achievements. A large set of unit tests and e2e test help me to keep the set of features intact in phases of code restructuring

Okay, as an Angular beginner, I deem I am far from being an ideal behavior driven Angular developer. However, at some point in future, I believe that I can increase my hobby development productivity by applying principles like BDD together with build&deployment automation based on TravisCI, CircleCI or a local Jenkins system to my development approach.

Overview

Along the way, we will get acquainted with a set of typical error messages and we will learn how to cope with them.

So, if you are ready for a quick ride into a simple “test first” strategy example with GIT repo handling, buckle up and start coding with me in four phases:

😉

  • Phase 1: Create a Hello World App based on Angular CLI
  • Phase 2: Adapt the end-to-end Tests
  • Phase 3: Adapt the Code
  • Phase 4: Verify the successful e2e Tests

If you do not care about BDD and GIT, then you might also want head over to the post Consuming a RESTful Web Service with Angular 4. Or better, follow the instructions you find here, but omit the steps related to e2e testing (protractor) and/or GIT.

Phase 1: Create a Hello World App based on Angular CLI

In this phase, we will

  • use an Angular CLI Docker image to create a new application
  • fix some problems with the end to end testing inherent in the standard hello world app
  • save and upload the changes to GIT

Step 1.0: Get access to a Docker Host with enough Resources

If you do not have access to a Docker host yet, I recommend following the step 0 instructions on my JHipster post. I recommend to use a Docker host with at least 1.5 GB RAM. To be honest, this is a guess. I always test on a 4 GB Docker Host Virtualbox VM, but I know that 750 MB RAM is not sufficient.

Step 1.1: Prepare an alias for later use

Let us first define an alias that helps us to shorten the commands thereafter.

(dockerhost)$ alias cli='docker run -it --rm -w /app -v $(pwd):/app -p 4200:4200 -u $(id -u $(whoami)) oveits/angular-cli:1.4.3 $@'

Why this complicated user option -u $(id -u $(whoami))? The reason is that

  • if we omit it, then all new files will be created as root, so we will get permissions problems later on
  • If we use ‘centos’, then the container will complain that he does not find the user ‘centos’ in its passwd file
  • If we use the ID of centos, then it works. However, it might not work in all cases. This time, the ID of centos user is 1000, and by chance, a user (named ‘node’) exists on the container as well. But let us live with this uncertainty for now.

With each cli something command, we will start a something command on an Angular CLI @ Alpine container originally created by Alex Such and enriched with git and bash by me.

Consider appending the alias command to your Docker host’s ~/.bashrc file, so the alias is persistent.

Step 1.2: Create a Project and install required Modules

Now let us create a new project and install the node modules via npm:

(dockerhost)$ cli ng new consuming-a-restful-web-service-with-angular
(dockerhost)$ cd consuming-a-restful-web-service-with-angular
(dockerhost)$ cli npm install
npm info it worked if it ends with ok
npm info using npm@3.10.10
npm info using node@v6.11.2
npm info attempt registry request try #1 at 7:54:24 PM
npm http request GET https://registry.npmjs.org/fsevents
npm http 200 https://registry.npmjs.org/fsevents
npm info lifecycle consuming-a-restful-web-service-with-angular@0.0.0~preinstall: consuming-a-restful-web-service-with-angular@0.0.0
npm info linkStuff consuming-a-restful-web-service-with-angular@0.0.0
npm info lifecycle consuming-a-restful-web-service-with-angular@0.0.0~install: consuming-a-restful-web-service-with-angular@0.0.0
npm info lifecycle consuming-a-restful-web-service-with-angular@0.0.0~postinstall: consuming-a-restful-web-service-with-angular@0.0.0
npm info lifecycle consuming-a-restful-web-service-with-angular@0.0.0~prepublish: consuming-a-restful-web-service-with-angular@0.0.0
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 (node_modules/chokidar/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.1.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm info ok

Step 1.3 (optional): Create a local GIT Repository

Now is a good time to create a git repository and to commit the initial code.

If you have not installed GIT on your Docker host, depending on the operating system of your Docker host, you might need to install it first (e.g. apt-get update; apt-get install -y git in case of Ubuntu, or yum install -y git in case of CentOS). Alternatively, you may want to use the git I have installed in the container. In that case, prepend a “do” before the git command, e.g. try cli git --version. However, a git diff does not look nice in a container, so I recommend to install GIT on your Docker host instead.

Now let us initialize the git repo, add all files and commit the changes:

(dockerhost)$ git init
(dockerhost)$ git add .
(dockerhost)$ git commit -m'initial commit'

Now let us start the service in a container:

(dockerhost)$ cli 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: 2017-09-26T20:04:45.036Z
Hash: 24fe32460222f3b3faf2
Time: 15376ms
chunk {inline} inline.bundle.js, inline.bundle.js.map (inline) 5.83 kB [entry] [rendered]
chunk {main} main.bundle.js, main.bundle.js.map (main) 8.88 kB {vendor} [initial] [rendered]
chunk {polyfills} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 209 kB {inline} [initial] [rendered]
chunk {styles} styles.bundle.js, styles.bundle.js.map (styles) 11.3 kB {inline} [initial] [rendered]
chunk {vendor} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.29 MB [initial] [rendered]

webpack: Compiled successfully.

Step 1.4: Perform end-to-end Tests

Step 1.4.1: Use a Protractor Docker Image to perform the Tests

In the spirit of “test first” strategies of “behavior-driven development”, let us check the end-to-end tests that come with Angular CLI 1.4.3. We will see that they are broken and need to be adapted.

Like above, we will use a Docker container for the task. This time we will use the Docker image protractor-headless from webnicer. In a second terminal, we first define an alias, enter the project root folder and run protractor.

(dockerhost)$ alias protractor='docker run -it --privileged --rm --net=host -v /dev/shm:/dev/shm -v $(pwd):/protractor webnicer/protractor-headless $@'
(dockerhost)$ cd consuming-a-restful-web-service-with-angular
(dockerhost)$ protractor

[20:20:34] I/direct - Using ChromeDriver directly...
[20:20:34] I/launcher - Running 1 instances of WebDriver
Jasmine started

  consuming-a-restful-web-service-with-angular App
    ✗ should display welcome message
      - Failed: Error while waiting for Protractor to sync with the page: "Could not find testability for element."
          at /usr/local/lib/node_modules/protractor/built/browser.js:272:23
          at ManagedPromise.invokeCallback_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1379:14)
          at TaskQueue.execute_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2913:14)
          at TaskQueue.executeNext_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2896:21)
          at asyncRun (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2775:27)
          at /usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:639:7
          at process._tickCallback (internal/process/next_tick.js:103:7)Error
          at ElementArrayFinder.applyAction_ (/usr/local/lib/node_modules/protractor/built/element.js:461:27)
          at ElementArrayFinder._this.(anonymous function) [as getText] (/usr/local/lib/node_modules/protractor/built/element.js:103:30)
          at ElementFinder.(anonymous function) [as getText] (/usr/local/lib/node_modules/protractor/built/element.js:829:22)
          at AppPage.getParagraphText (/protractor/e2e/app.po.ts:9:43)
          at Object. (/protractor/e2e/app.e2e-spec.ts:12:17)
          at /usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:94:23
          at new ManagedPromise (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1082:7)
          at controlFlowExecute (/usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:80:18)
          at TaskQueue.execute_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2913:14)
          at TaskQueue.executeNext_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2896:21)
      From: Task: Run it("should display welcome message") in control flow
          at Object. (/usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:79:14)
          at /usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:16:5
          at ManagedPromise.invokeCallback_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1379:14)
          at TaskQueue.execute_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2913:14)
          at TaskQueue.executeNext_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2896:21)
          at asyncRun (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2775:27)
      From asynchronous test:
      Error
          at Suite. (/protractor/e2e/app.e2e-spec.ts:10:3)
          at Object. (/protractor/e2e/app.e2e-spec.ts:3:1)
          at Module._compile (module.js:570:32)
          at Module.m._compile (/protractor/node_modules/ts-node/src/index.ts:392:23)
          at Module._extensions..js (module.js:579:10)
          at Object.require.extensions.(anonymous function) [as .ts] (/protractor/node_modules/ts-node/src/index.ts:395:12)

**************************************************
*                    Failures                    *
**************************************************

1) consuming-a-restful-web-service-with-angular App should display welcome message
  - Failed: Error while waiting for Protractor to sync with the page: "Could not find testability for element."

Executed 1 of 1 spec (1 FAILED) in 0.878 sec.
[20:20:41] I/launcher - 0 instance(s) of WebDriver still running
[20:20:41] I/launcher - chrome #01 failed 1 test(s)
[20:20:41] I/launcher - overall: 1 failed spec(s)
[20:20:41] E/launcher - Process exited with error code 1

Even though my application is listening on port 4200,  we can see that the e2e tests have a problem.

Step 1.4.2: Correct the Protractor sync Issue

As already pointed out in this blog post, we need to add the option

useAllAngular2AppRoots: true

to our protractor.conf.js file. At the end, the file has following content (correction in blue):

// protractor.conf.js
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts

const { SpecReporter } = require('jasmine-spec-reporter');

exports.config = {
  allScriptsTimeout: 11000,
  specs: [
    './e2e/**/*.e2e-spec.ts'
  ],
  capabilities: {
    'browserName': 'chrome'
  },
  directConnect: true,
  baseUrl: 'http://localhost:4200/',
  useAllAngular2AppRoots: true,
  framework: 'jasmine',
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000,
    print: function() {}
  },
  onPrepare() {
    require('ts-node').register({
      project: 'e2e/tsconfig.e2e.json'
    });
    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
  }
};

After that, the e2e test is still not successful:

$ protractor
[20:30:32] I/direct - Using ChromeDriver directly...
[20:30:32] I/launcher - Running 1 instances of WebDriver
Jasmine started

  consuming-a-restful-web-service-with-angular App
    ✗ should display welcome message
      - Expected 'Welcome to !' to equal 'Welcome to app!'.
          at Object. (/protractor/e2e/app.e2e-spec.ts:12:37)
          at /usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:94:23
          at new ManagedPromise (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1082:7)
          at controlFlowExecute (/usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:80:18)
          at TaskQueue.execute_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2913:14)
          at TaskQueue.executeNext_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2896:21)
          at asyncRun (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2820:25)
          at /usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:639:7
          at process._tickCallback (internal/process/next_tick.js:103:7)

**************************************************
*                    Failures                    *
**************************************************

1) consuming-a-restful-web-service-with-angular App should display welcome message
  - Expected 'Welcome to !' to equal 'Welcome to app!'.

Executed 1 of 1 spec (1 FAILED) in 0.848 sec.
[20:30:40] I/launcher - 0 instance(s) of WebDriver still running
[20:30:40] I/launcher - chrome #01 failed 1 test(s)
[20:30:40] I/launcher - overall: 1 failed spec(s)
[20:30:40] E/launcher - Process exited with error code 1

Step 1.4.3: Correct the e2e Test Script

The reason is that the app.component.ts file is not correct. In the HTML template, we find a line

Welcome to {{title}}!

but in the component file, the title is missing:

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

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

  constructor() { }

  ngOnInit() {
  }

}

This is leading to following corrupt web page:

Let us correct this now (in blue):

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

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

  title : any = null

  constructor() { }

  ngOnInit() {
     this.title = "app";
  }

}

Now the Web page looks better:

Now the e2e tests are successful:

$ protractor
[20:53:42] I/direct - Using ChromeDriver directly...
[20:53:42] I/launcher - Running 1 instances of WebDriver
Jasmine started

  consuming-a-restful-web-service-with-angular App
    ✓ should display welcome message

Executed 1 of 1 spec SUCCESS in 0.956 sec.
[20:53:50] I/launcher - 0 instance(s) of WebDriver still running
[20:53:50] I/launcher - chrome #01 passed

The angular CLI installation works as expected now.

Excellent! Thump up!

Let us save the changes:

(dockerhost)$ git add protractor.conf.js
(dockerhost)$ git commit -m'protractor.conf.js: added useAllAngular2AppRoots: true for avoiding sync problems'
(dockerhost)$ git add src/app/app.component.ts
(dockerhost)$ git commit -m'app component: defined missing title'

Now is the time to sign up with Github and save the project. In my case, I have created following project: a project like follows:

https://github.com/oveits/consuming-a-restful-web-service-with-angular

Once this is done, we can upload the changes as follows:

(dockerhost)$ git remote add origin https://github.com/oveits/consuming-a-restful-web-service-with-angular.git
(dockerhost)$ git push -u origin master

Phase 2: Adapt the end-to-end Tests

In this phase, we will

  • based on the input from the WordPress API, we will plan, how the web page should look like from a customer’s point of view.
  • We will adapt the e2e tests, so they reflect the (assumed) customer’s expectations.
  • We will save the changed code to the remote GIT repository.

Step 2.1: Planning

In an attempt to follow a behavior driven development process, we will write/adapt the end to end tests first, before we perform the changes. For this, let us outline our plan:

  • We would like to create a Web page that displays the title and content of a WordPress Article
  • the WordPress article of our choice is the first angular blog post I have written: the Angular 4 Hello World Quickstart blog post
  • The article will be retrieved dynamically from the WordPress API, a REST API.

Step 2.2: Explore the WordPress REST API

Let us have a look at the WordPress API. The WordPress API can be explored via the WordPress.com REST API console. We can display a list of blog posts like so:

We can see that the blog post we would like to display has the ID 3078 and the title and content star like follows:

  • title: “Angular 4 Hello World Quickstart”
  • content: “<p>In this hello world style tutorial, we will follow a step by step guide to a working Angular 4 application. We will also …

The  single blog post can be retrieved with the URL

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

We can verify this by copying the URL into a Browser:

Step 2.3: Adapt the end-to-end Tests

With the knowledge about the title and content of the blog post, we can re-write the end-to-end (e2e) test. The e2e test is found in the e2e folder:

ls e2e/
app.e2e-spec.ts app.po.ts tsconfig.e2e.json

$ cat e2e/app.e2e-spec.ts
import { AppPage } from './app.po';

describe('consuming-a-restful-web-service-with-angular App', () => {
  let page: AppPage;

  beforeEach(() => {
    page = new AppPage();
  });

  it('should display welcome message', () => {
    page.navigateTo();
    expect(page.getParagraphText()).toEqual('Welcome to app!');
  });
});

Instead of searching for the text ‘Welcome to app’, let us search for the title “Angular 4 Hello World Quickstart”:

$ cat e2e/app.e2e-spec.ts
import { AppPage } from './app.po';

describe('consuming-a-restful-web-service-with-angular App', () => {
  let page: AppPage;

  beforeEach(() => {
    page = new AppPage();
  });

  it('should display the title', () => {
    page.navigateTo();
    expect(page.getParagraphText()).toContain('Angular 4 Hello World Quickstart');
  });
});

The e2e test should fail now with the message Expected 'Welcome to app!' to contain 'Angular 4 Hello World Quickstart'

$ protractor
[20:46:02] I/direct - Using ChromeDriver directly...
[20:46:02] I/launcher - Running 1 instances of WebDriver
Jasmine started

  consuming-a-restful-web-service-with-angular App
    ✗ should display welcome message
      - Expected 'Welcome to app!' to contain 'Angular 4 Hello World Quickstart'.
          at Object. (/protractor/e2e/app.e2e-spec.ts:12:37)
          at /usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:94:23
          at new ManagedPromise (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1082:7)
          at controlFlowExecute (/usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:80:18)
          at TaskQueue.execute_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2913:14)
          at TaskQueue.executeNext_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2896:21)
          at asyncRun (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2820:25)
          at /usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:639:7
          at process._tickCallback (internal/process/next_tick.js:103:7)

**************************************************
*                    Failures                    *
**************************************************

1) consuming-a-restful-web-service-with-angular App should display welcome message
  - Expected 'Welcome to app!' to contain 'Angular 4 Hello World Quickstart'.

Executed 1 of 1 spec (1 FAILED) in 0.907 sec.
[20:46:21] I/launcher - 0 instance(s) of WebDriver still running
[20:46:21] I/launcher - chrome #01 failed 1 test(s)
[20:46:21] I/launcher - overall: 1 failed spec(s)
[20:46:21] E/launcher - Process exited with error code 1

Step 2.4: Save the Changes on a separate GIT Branch

We believe that the e2e tests are correct now, so it is a good time to create a new git feature branch and commit the code:

git checkout -b feature/0001-retrieve-and-display-WordPress-title-from-API
git add .
git commit -m'adapted e2e tests to display WordPress blog title'
git push

Phase 3: Adapt the Code

Now, after having written the e2e tests, let us change the code, so our app fulfills the expectations.

Step 3.1: Define the HTML View

In the spirit of a behavior driven approach, let us define the view first. For that we replace the content of the app’s template file:

$ cat src/app/app.component.html
<h1>{{title}}</h1>

The output of the application now is:

This is because, in the Hello World app, we have set the title to the static value ‘app’. The e2e tests are not successful and the error ‘Expected ‘app’ to contain ‘Angular 4 Hello World Quickstart’.’ is thrown when we run protractor.

Step 3.2: Subscribe an Observable

As can be seen in many tutorials, we now subscribe to an observable like follows:

$ cat src/app/app.component.ts
import { Component, OnInit } from '@angular/core';

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

  title : any = null

  constructor() { }

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

}

We perform an HTTP GET on the WordPress API’s URL, map the response to a JSON object and subscribe the retrieved data. The data should contain a title, which we assign to the local title variable.

However, we will see in the log:

ERROR in /app/src/app/app.component.ts (16,11): Property '_http' does not exist on type 'AppComponent'.

And in the browser, we see:

Let us fix that now.

Step 3.3: Define private local _http Variable

In angular, we can define the private local _http variable in the constructor:

constructor(private _http: Http) {}

Once, this is done, the error message is changed to:

ERROR in /app/src/app/app.component.ts (12,30): Cannot find name 'Http'.

Step 3.4: Import Http Components

The used Http module is not known to our app component. Let us change this now. We add the following line

import { Http } from '@angular/http';

to the file src/app/app.component.ts. The error message changes to:

ERROR in /app/src/app/app.component.ts (18,18): Property 'map' does not exist on type 'Observable<Response>'.

Step 3.5: Import map

The map function needs to be imported as well:

import 'rxjs/add/operator/map'

Now we get an illegible error like follows:

ERROR in /app/src/app/app.component.ts (18,6): The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly.
  Type argument candidate 'Response' is not a valid type argument because it is not a supertype of candidate 'Response'.
    Types of property 'type' are incompatible.
      Type 'ResponseType' is not assignable to type 'ResponseType'. Two different types with this name exist, but they are unrelated.
        Type '"basic"' is not assignable to type 'ResponseType'.

Step 3.6: Import Response Type

We finally can get rid of the quite illegible error message by adding another import:

import { Response } from '@angular/http';

However, this still does not lead to the desired result. In the browser we see an empty page:

and the e2e tests fail with the following message:

$ protractor
...
Failed: Angular could not be found on the page http://localhost:4200/. If this is not an Angular application, you may need to turn off waiting for Angular. Please see https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load

Step 3.7: Add HttpModule in the app Module

The solution of the above error lies in the src/app/app.module.ts (added parts in blue). We first need to add the HttpModule to the imports, which alters the error message to

$ cat src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule }    from '@angular/http';

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

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

This seems to have been the last stepping stone towards success:

Phase 4: Verify the successful e2e Tests

Now the e2e tests are successful:

$ protractor
[22:16:06] I/direct - Using ChromeDriver directly...
[22:16:06] I/launcher - Running 1 instances of WebDriver
Jasmine started

  consuming-a-restful-web-service-with-angular App
    ✓ should display welcome message

Executed 1 of 1 spec SUCCESS in 1 sec.
[22:16:14] I/launcher - 0 instance(s) of WebDriver still running
[22:16:14] I/launcher - chrome #01 passed

That is, how the e2e Tests should look like. Success!

Excellent! Thump up!

Step 4.2: Save the changes to the develop branch on GIT

Since our new feature “retrieve and display a blog title from WordPress API” has been verified to work fine, it is time to commit the change and push it to the remote repository:

git add .
git commit -m'added all code needed for successful e2e tests'
git push
git checkout -b develop
git push

In addition to that, we can create a new “develop” branch, if it does not exist yet:

git checkout -b develop
git push

In case the develop branch exist already, you need to merge the code to the instead of creating the develop branch:

git checkout develop
git merge feature/0001-retrieve-and-display-WordPress-title-from-API
git push

It makes sense to allow a merge to the develop branch only if the code is fully tested. This way, we will never break the code in the develop branch.

For large teams, several measures can be taken to make sure that only high quality code enters the develop branch: e.g. on BitBucket GIT, you can allow a merge only, if code has been reviewed and acknowledged by a certain number of team members. Moreover, you can integrate the repository with a Jenkins system: with the correct plugins, you can make sure that a merge is allowed only in case all quality gates (e2e test, unit tests, style, performance, …) in the Jenkins pipeline are met.

However, if you are a hobby developer working on a , it is probably sufficient if you run the tests manually before you merge the changed code into the develop or master branch.

Summary

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

  • How to create a new Hello World project using Angular CLI, repair the e2e tests and save the changes on GIT.
  • How to create/adapt the e2e tests in advance a “test first” manner.
  • How to consume a REST service using Angular 4 and verify the result using the e2e test scripts we have created before.

Next Steps

In part 2 of this series, we will learn how to add and display HTML content to the body of our application. We will see that we cannot just use the method we have used for the title. If we do so, we will see escaped HTML code like follows:

<p>In this hello world style tutorial,…

We will show how to make Angular accept the HTML code and display it correctly.

References:

Appendix A: Adding Docker Support

This is, how I have added Docker support for the application, following my tl:dr of the blog post Angular 4 Docker Example.

A.1 Add Dockerfile and NginX config

git clone https://github.com/oveits/consuming-a-restful-web-service-with-angular
git checkout -b feature/0002-docker-support
curl -O https://raw.githubusercontent.com/avatsaev/angular4-docker-example/master/Dockerfile
curl -O https://raw.githubusercontent.com/avatsaev/angular4-docker-example/master/nginx/default.conf
mkdir nginx
mv default.conf nginx/

remove ‘package-log.json’ from Dockerfile

git add .
git commit -m 'added Dockerfile and nginx config file'
git push

A.2 Build the Docker  Image

On a docker host, I have issued following commands:

docker build . --tag oveits/consuming-a-restful-web-service-with-angular:v0.2
docker push oveits/consuming-a-restful-web-service-with-angular:v0.2
docker tag oveits/consuming-a-restful-web-service-with-angular:v0.2 oveits/consuming-a-restful-web-service-with-angular:latest
docker push oveits/consuming-a-restful-web-service-with-angular:v0.2
docker push oveits/consuming-a-restful-web-service-with-angular:latest

A.3 Run the Service

$ alias consuming='docker run --rm --name consuming-a-restful-web-service-with-angular -d -p 80:80 oveits/consuming-a-restful-web-service-with-angular $@'
$ consuming

A.4 Access the Service

In a browser, head to the public DNS of the image:

Works!

Excellent! Thump up!

 

 

0

Companies that Mimic Life Inherently Apply Management 3.0 and Belong to the Most Successful Companies


Consider the following seven companies:
– Unilever – a producer of household goods
– Nucor – a global steel producer
– United Technologies – a manufacturer of capital goods
– Novo Nordisk – a player in the global pharmaceutical industry
– Henkel – a world leader in applied chemistry and adhesives technology
– Nike – a manufacturer of athletic wear
– Westpac Banking – a bank

What do those companies have in common? Companies from different industries with different ages. What could it be that links them together? The answer is, that they all belong to the commercially most successful companies of the last 20 years. All of them have outperformed their primary competitors and global benchmark indices. Ok, this is good, but not yet something that’s special. So, what is it that makes them really special? It is their outstanding success which is based on a disruptive approach and implementation of management and leadership. They all consider organizations as living systems, in contrast to the widespread and common organizations-as-machine ideal, which was dominant for hundreds of years. They belong to those companies that are comprised under the umbrella Companies that Mimic Life.

Their success is not just based on gut feeling, but on hard statistically significant facts. In 1995 Joseph H. Bragdon created a learning lab of companies. Companies that place a higher value on living assets (people and Nature) than on nonliving capital assets. These learning lab companies, where the mentioned seven ones are part of, practice something Bragdon calls living asset stewardship (LAS); they nurture people and Nature instead of exploiting them; they maximize the speed of learning and adaptation; they aim to radically lower their global ecological footprint. In addition, these learning lab companies comprise the Global Living Asset Management Performance (LAMP) Index that Bragdon introduced in 1996. As of today this index consists of 60 companies who all adhere to LAS. What makes this index so special is that between 1996 and 2015 the LAMP index has outperformed three of the most commonly used global equity benchmarks, namely the MSCI World Index, the FTSE World Index, and the S&P Global 100 Index. But that’s not the whole story. As good as this sounds, the learnings through the lab go much deeper. For example, one more outcome is that the more a company adheres to LAS the more profitable it becomes.

But what is the powerful secret? What is it that makes up this outstanding performance? Which factors lay behind LAS? It’s something that’s easy to understand but apparently hard to implement. The companies’ extraordinary success is due to the following six critical lifelike qualities, identified by Bragdon, that are present in all life, from single-cell organisms to large ecosystems:
– Decentralized, self-organizing networked structures
– Regenerative life strategies
– Frugal instincts
– Openness to feedback
– Symbiotic behaviors
– Consciousness

All of these qualities are in some kind apparent in the LAMP index companies. Now, if we look at the six critical lifelike qualities from a management perspective this sounds very familiar to people who know Management 3.0. Management 3.0 is packed by the systems and complexity theory. From systems and complexity theory we know that the increasing complexity and fast-changing environments cannot be handled or controlled though a complicated model or framework. There are no laws for dealing with change, uncertainty and complexity, only guidelines we can follow. In his famous book Management 3.0 published in 2010, Jurgen Appelo presents a management model of the same name that successfully addresses those challenges. Appelo’s Management 3.0 model recognizes that today’s organizations are living, networked systems; and that management is primarily about people and their relationships. Grounded on the complexity and system theory Appelo identified six areas of management responsibilities and provides guidance on how to deal with them. Those areas are as follows: Energize People; Empower Teams; Align Constraints; Develop Competence; Grow Structure, Improve Everything.

But what’s really impressive with Management 3.0 is that the six critical lifelike qualities defined by Bragdon are inherent in the Management 3.0 model. Let’s look at them one by one.

# Decentralized, self-organizing networked structures
From complexity theory we know that a complex environment cannot be controlled through a central authority. Decisions need to be delegated to the parts of the system. Organizations need to empower their teams and employees, they need to decentralize decision-making to be able to survive in those fast-changing business environments. Self-organizing teams at the frontline ensure the effective treatment with challenging situations. Network structures ensure fast temporary reorganizations necessary to deal effectively with challenges while distributing information from end-to-end effectively. Like a blade of grass in the wind, bending with the wind in a storm, but standing upright again when the storm is gone, network structures will reshape according to challenges and afterwards resume the previous structure. Decentralized, self-organizing networked structures are a central point of the Management 3.0 model.

# Regenerative life strategies
Regenerative life strategies are especially expressed in learning and information gathering in companies. Creating a culture that enables employees to permanently learn and improve strengthens a firm’s survival. Regenerating the lived interactions between people leads to new strategies best addressing the constantly changing business environment companies live in.

# Frugal instincts
Frugal instincts addressed at the point where things happen lead to fast and effective corrective actions undertaken by empowered employees. Frugal instincts are also an ingredient of servant leaders, superiors who act as coach and mentor instead of being bossy.

# Openness to feedback
Openness to feedback is the foundation to improve everything. Without openness and transparency organizations will not be able to find ways to improve. Openness is also key to delegate decisions to the frontline teams, to develop competence, to energize people, to align values and culture, and to grow the organization.

# Symbiotic behaviors
High performance, self-organized teams inherently exhibit symbiotic behavior. Without cooperation and trust in each other they would not achieve their great success. The symbiotic behavior of teams is the DNA that leads to excellent, outperforming results and delivers the flexibility to deal effectively with unforeseen and unexpected challenges.

# Consciousness
Consciousness is a fundamental property of all human beings. And people and their relationships is the core principle behind all aspects of the Management 3.0 model. Consciousness is the foundation of a living Management 3.0 organization.

Companies that mimic life, that place a higher value on living assets (people and Nature), inherently apply the Management 3.0 model. Considering Bragdon’s learning lab and Global Living Asset Management Performance Index, we can conclude that Management 3.0 applied leads to higher profits with happier employees while at the same time preserving our earth for and the future of our children.

 

0

Angular end to end Testing


– Angular e2e Protractor Tests on Systems without GUI applied to static and dynamic Web Pages –

In this blog post, we will show how to perform end-to-end (e2e) tests with Angular 4:

  • first, we will apply Protractor end to end tests on a little Angular CLI hello world application with static HTML content
  • second, we will perform end to end tests for a dynamic web application, a REST client application that retrieves and displays content received via the WordPress API.

For that, we will use a Docker Protractor Image that can be used on systems with or without graphical user interface (e.g. a Jenkins CI system).

 

Protractor + Docker

In a classical installation situation, you often use a machine with a graphical window system (e.g. X11 for Linux) so you can run a real Chrome or Firefox Browser on the system. In such a situation, you often install Jasmine, Protractor, Selenium and a Chrome or Firefox browser on the test machine. However, in this blog post, we prefer to use a pre-installed Docker image provided by webnicer instead, which allows us to run the e2e tests on Docker hosts without a graphical system. The missing need for a graphical interface makes this an ideal deployment option for continuous integration purposes, e.g. for Jenkins systems.

Why End to End Tests?

My motivation to write a blog about end-to-end tests is, that I have made a very good experience with “tests first” and more specifically “behavior driven development” (BDD) principles with previous projects. And end-to-end tests are the main ingredient needed for BDD. In my experience, projects that follow “test first” or BDD principles benefit from a better customer view and higher quality, together with higher motivated developers, who start their work with a set of failed (red) tests and are rewarded for their work with successful (green) tests.

Even if we are far from following behavior driven principles yet, let us perform our first tiny steps towards this principle by looking more closely at end-to-end testing now:

Step 1: Install and start an Angular CLI Hello World Application

We are closely following the phase 1 instructions on a previous popular blog post of mine Consuming a RESTful Web Service with Angular 4. Since I do not want to copy and paste that part of the other blog post, let me just summarize the commands after you have got access to a Docker host (e.g. by following the instructions found here; search for the term “Install a Docker Host”):

  • start Docker container
docker run -it -p 4200:4200 -v $(pwd):/localdir centos bash
  • install Angular CLI
(container)# yum install -y epel-release
(container)# yum install -y https://kojipkgs.fedoraproject.org//packages/http-parser/2.7.1/3.el7/x86_64/http-parser-2.7.1-3.el7.x86_64.rpm
(container)# yum install -y nodejs 
(container)# npm install -g @angular/cli
  • create a project
(container)# cd /localdir
(container)# ng new my-project-name
(container)# cd my-project-name
  • we do not yet start the service in order to see a certain error message, but in step 3, we will start the service with this command:
(container)# ng serve --host 0.0.0.0

For more detailed information about Step 1, see phase 1 of this blog post.

Step 2: Prepare Docker Host for Protractor Usage

For a convenient handling of the protractor test, let us open a new terminal and create a shell script on the Docker host like follows (see the readme of the Docker image page webnicer/protractor-headless):

If your Docker host does not allow the usage of sudo, then try the same commands without sudo.

(dockerhost)$ sudo cat - << END | sudo tee /usr/local/bin/protractor-headless
#!/bin/bash
docker run -it --privileged --rm --net=host -v /dev/shm:/dev/shm -v \$(pwd):/protractor webnicer/protractor-headless \$@
END

Then we make sure the file is executable:

(dockerhost)$ sudo chmod +x /usr/local/bin/protractor-headless
(dockerhost)$ which /usr/local/bin/protractor-headless
/usr/local/bin/protractor-headless

Step 3: Download and run Protractor in a Docker Container

Let us download the protractor Docker image webnicer/protractor-headless so the container will be started immediately from the image later:

(dockerhost)$ docker pull webnicer/protractor-headless 

Then we enter the project root folder we have created in step 1:

(dockerhost)$ cd my-project-name

and run protractor via:

(dockerhost)$ protractor-headless
[23:43:08] I/direct - Using ChromeDriver directly...
[23:43:08] I/launcher - Running 1 instances of WebDriver
Spec started
[23:43:24] E/protractor - Could not find Angular on page http://localhost:4200/ : retries looking for angular exceeded

  my-project-name App
    ✗ should display welcome message
      - Failed: Angular could not be found on the page http://localhost:4200/. If this is not an Angular application, you may need to turn off waiting for Angular. Please see https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load
          at /usr/local/lib/node_modules/protractor/built/browser.js:506:23
          at ManagedPromise.invokeCallback_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1379:14)
          at TaskQueue.execute_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2913:14)
          at TaskQueue.executeNext_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2896:21)
          at asyncRun (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2775:27)
          at /usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:639:7
          at process._tickCallback (internal/process/next_tick.js:103:7)
      From: Task: Run it("should display welcome message") in control flow
          at Object. (/usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:79:14)
          at /usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:16:5
          at ManagedPromise.invokeCallback_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1379:14)
          at TaskQueue.execute_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2913:14)
          at TaskQueue.executeNext_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2896:21)
          at asyncRun (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2775:27)
      From asynchronous test:
      Error
          at Suite. (/protractor/e2e/app.e2e-spec.ts:10:3)
          at Object. (/protractor/e2e/app.e2e-spec.ts:3:1)
          at Module._compile (module.js:570:32)
          at Module.m._compile (/protractor/node_modules/ts-node/src/index.ts:382:23)
          at Module._extensions..js (module.js:579:10)
          at Object.require.extensions.(anonymous function) [as .ts] (/protractor/node_modules/ts-node/src/index.ts:385:12)

**************************************************
*                    Failures                    *
**************************************************

1) my-project-name App should display welcome message
  - Failed: Angular could not be found on the page http://localhost:4200/. If this is not an Angular application, you may need to turn off waiting for Angular. Please see https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load

Executed 1 of 1 spec (1 FAILED) in 10 secs.
[23:43:24] I/launcher - 0 instance(s) of WebDriver still running
[23:43:24] I/launcher - chrome #01 failed 1 test(s)
[23:43:24] I/launcher - overall: 1 failed spec(s)
[23:43:24] E/launcher - Process exited with error code 1

Okay, this is expected, because no angular is running on port 4200 yet.

Step 4: Start Angular CLI Application and run Protractor again

As anticipated, let us start the Angular application as describes in step 1:

(container)# ng serve --host 0.0.0.0

Then we get following output, if we try to run the e2e tests:

(dockerhost)$ protractor-headless
[00:19:21] I/direct - Using ChromeDriver directly...
[00:19:21] I/launcher - Running 1 instances of WebDriver
Spec started

  my-project-name App
    ✗ should display welcome message
      - Failed: Error: Error while waiting for Protractor to sync with the page: "Could not find testability for element."
          at proxyDone.fail (/usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:87:34)
          at ManagedPromise.invokeCallback_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1379:14)
          at TaskQueue.execute_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2913:14)
          at TaskQueue.executeNext_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2896:21)
          at asyncRun (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2775:27)
          at /usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:639:7
          at process._tickCallback (internal/process/next_tick.js:103:7)
      From: Task: Run it("should display welcome message") in control flow
          at Object. (/usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:79:14)
          at /usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:16:5
          at ManagedPromise.invokeCallback_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1379:14)
          at TaskQueue.execute_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2913:14)
          at TaskQueue.executeNext_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2896:21)
          at asyncRun (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2775:27)
      From asynchronous test:
      Error
          at Suite. (/protractor/e2e/app.e2e-spec.ts:10:3)
          at Object. (/protractor/e2e/app.e2e-spec.ts:3:1)
          at Module._compile (module.js:570:32)
          at Module.m._compile (/protractor/node_modules/ts-node/src/index.ts:382:23)
          at Module._extensions..js (module.js:579:10)
          at Object.require.extensions.(anonymous function) [as .ts] (/protractor/node_modules/ts-node/src/index.ts:385:12)

**************************************************
*                    Failures                    *
**************************************************

1) my-project-name App should display welcome message
  - Failed: Error: Error while waiting for Protractor to sync with the page: "Could not find testability for element."

Executed 1 of 1 spec (1 FAILED) in 0.565 sec.
[00:19:26] I/launcher - 0 instance(s) of WebDriver still running
[00:19:26] I/launcher - chrome #01 failed 1 test(s)
[00:19:26] I/launcher - overall: 1 failed spec(s)
[00:19:26] E/launcher - Process exited with error code 1

Step 5: Fix the “waiting for Protractor to sync” Problem and run Protractor again

I have found a solution on this StackOverflow Q&A: we need to add the additional configuration line into protractor.conf.js:

useAllAngular2AppRoots: true

In my case, the protractor configuration file looks like follows:

// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts

const { SpecReporter } = require('jasmine-spec-reporter');

exports.config = {
  allScriptsTimeout: 11000,
  specs: [
    './e2e/**/*.e2e-spec.ts'
  ],
  capabilities: {
    'browserName': 'chrome'
  },
  directConnect: true,
  baseUrl: 'http://localhost:4200/',
  useAllAngular2AppRoots: true,
  framework: 'jasmine',
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000,
    print: function() {}
  },
  onPrepare() {
    require('ts-node').register({
      project: 'e2e/tsconfig.e2e.json'
    });
    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
  }
};

After that, the e2e test via Protractor Docker Container is successful:

protractor-headless
[00:46:32] I/direct - Using ChromeDriver directly...
[00:46:32] I/launcher - Running 1 instances of WebDriver
Spec started
[00:46:38] W/element - more than one element found for locator By(css selector, app-root h1) - the first result will be used

  my-project-name App
    ✓ should display welcme message

Executed 1 of 1 spec SUCCESS in 1 sec.
[00:46:38] I/launcher - 0 instance(s) of WebDriver still running
[00:46:38] I/launcher - chrome #01 passed

Excellent! Thump up!

Step 6: Review the Protractor Spec File

Now, we want to understand in more detail, what happened. For that, let us analyze the e2e specification file (e2e/app.e2e-spec.ts):

import { MyProjectNamePage } from './app.po';

describe('my-project-name App', () => {
  let page: MyProjectNamePage;

  beforeEach(() => {
    page = new MyProjectNamePage();
  });

  it('should display welcome message', done => {
    page.navigateTo();
    page.getParagraphText()
      .then(msg => expect(msg).toEqual('Welcome to app!!'))
      .then(done, done.fail);
  });
});

Now let us compare that with the browser content on http://localhost:4200:

There, it is: the e2e test is loading the page, retrieving the first paragraph and will compare it with the text “Welcome to app!!”. Since the text of the first paragraph matches this text, the test is successful.

Step 7 (optional): Review the Error Message of a failed Test

Now let us see, what happens, if we change the expected text in e2e/app.e2e-spec.ts to “Welcome to ppa!!” instead:

$ protractor-headless
[00:32:30] I/direct - Using ChromeDriver directly...
[00:32:30] I/launcher - Running 1 instances of WebDriver
Spec started
[00:32:36] W/element - more than one element found for locator By(css selector, app-root h1) - the first result will be used

  my-project-name App
    ✗ should display welcome message
      - Expected 'Angular 4 Hello World Quickstart' to equal 'Welcome to ppa!!'.
          at /protractor/e2e/app.e2e-spec.ts:13:32
          at /usr/local/lib/node_modules/protractor/built/element.js:798:32
          at ManagedPromise.invokeCallback_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1379:14)
          at TaskQueue.execute_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2913:14)
          at TaskQueue.executeNext_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2896:21)
          at asyncRun (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2775:27)
          at /usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:639:7
          at process._tickCallback (internal/process/next_tick.js:103:7)

**************************************************
*                    Failures                    *
**************************************************

1) my-project-name App should display welcome message
  - Expected 'Angular 4 Hello World Quickstart' to equal 'Welcome to ppa!!'.

Executed 1 of 1 spec (1 FAILED) in 1 sec.
[00:32:36] I/launcher - 0 instance(s) of WebDriver still running
[00:32:36] I/launcher - chrome #01 failed 1 test(s)
[00:32:36] I/launcher - overall: 1 failed spec(s)
[00:32:36] E/launcher - Process exited with error code 1

So, this is, how a failed test looks like.

Step 8 (advanced): Apply the e2e Test to a dynamic Web Page

Up to now, we have seen a simple test, which is comparing a static pattern with a static web page. We now want to apply the principle to a dynamic page as we have created in my blog post Consuming a RESTful Web Service with Angular 4. For that, we reverse the change in Step 7, so we get a successful e2e test again. Then we need to follow the steps in the corresponding blog post and run the resulting service on localhost port 4200. After that, the e2e test will fail:

(dockerhost)$ protractor-headless
[00:32:30] I/direct - Using ChromeDriver directly...
[00:32:30] I/launcher - Running 1 instances of WebDriver
Spec started
[00:32:36] W/element - more than one element found for locator By(css selector, app-root h1) - the first result will be used

  my-project-name App
    ✗ should display welcome message
      - Expected 'Angular 4 Hello World Quickstart' to equal 'Welcome to app!!'.
          at /protractor/e2e/app.e2e-spec.ts:13:32
          at /usr/local/lib/node_modules/protractor/built/element.js:798:32
          at ManagedPromise.invokeCallback_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1379:14)
          at TaskQueue.execute_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2913:14)
          at TaskQueue.executeNext_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2896:21)
          at asyncRun (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2775:27)
          at /usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:639:7
          at process._tickCallback (internal/process/next_tick.js:103:7)

**************************************************
*                    Failures                    *
**************************************************

1) my-project-name App should display welcome message
  - Expected 'Angular 4 Hello World Quickstart' to equal 'Welcome to app!!'.

Executed 1 of 1 spec (1 FAILED) in 1 sec.
[00:32:36] I/launcher - 0 instance(s) of WebDriver still running
[00:32:36] I/launcher - chrome #01 failed 1 test(s)
[00:32:36] I/launcher - overall: 1 failed spec(s)
[00:32:36] E/launcher - Process exited with error code 1

But what is the expected result we want to see? Let us head to http://localhost:4200 again, and we will see:

The blue part is static content again. However, the Title “Angular 4 Hello World Quickstart” is dynamic content retrieved from the WordPress API. We can easily see that this pattern is missing in the source HTML code:

Nevertheless, we want to use our Protractor Docker Container to test, whether the dynamic content is visible in a browser. For that, let us adapt the e2e test file on (e2e/app.e2e-spec.ts) to the new situation:

import { MyProjectNamePage } from './app.po';

describe('my-project-name App', () => {
  let page: MyProjectNamePage;

  beforeEach(() => {
    page = new MyProjectNamePage();
  });

  it('should display title', done => {
    page.navigateTo();
    page.getParagraphText()
      .then(msg => expect(msg).toContain('Angular 4 Hello World Quickstart'))
      .then(done, done.fail);
  });
});

the e2e test is successful:

(dockerhost)$ protractor-headless
[00:46:32] I/direct - Using ChromeDriver directly...
[00:46:32] I/launcher - Running 1 instances of WebDriver
Spec started
[00:46:38] W/element - more than one element found for locator By(css selector, app-root h1) - the first result will be used

  my-project-name App
    ✓ should display title

Executed 1 of 1 spec SUCCESS in 1 sec.
[00:46:38] I/launcher - 0 instance(s) of WebDriver still running
[00:46:38] I/launcher - chrome #01 passed

Excellent! Thump up!

We have achieved our goal: we had intended to perform a successful end to end test for a dynamic web page that retrieves its content from an external resource like the WordPress API.

Summary

In this blog post, we

  • have applied a Protractor Docker Container on an existing Angular CLI Hello World.
    For this to work, we had to adapt the Protractor configuration file to circumvent an “Error while waiting for Protractor to sync with the page”
  • have applied a Protractor Docker Container with the patched Protractor configuration on an app that is dynamically downloading and displaying content retrieved from the WordPress API. This has worked as expected although the content we have looked for was not visible in the HTML source code.

With the latter, we have successfully verified that the Protractor Docker container can handle dynamic content retrieved and altered via javascript.

Next Steps

  • Write more complete test cases for the dynamic web page
  • Refactor the dynamic web page application by dividing the HTTP GET into a separate service. The Protractor tests will help me to verify that the refactored application will keep its previously achieved features.

References:

0

JHipster: Exploring and Improving the Spring Boot REST API


In our last blog post, we have created a simple blog application using JHipster, which is a Yeoman based code generator for Angular and Spring Boot. This time, we will explore and improve the Spring Boot REST API that is generated by JHipster automatically.

In the last blog, we had created following model; a simple blog:

In this blog post, we will concentrate on the “Blog” objects. We will try to create, read, update and delete such objects. We will find out that any user has all rights to perform those function, and we will make sure that only the blog owner can update and delete the objects.

Step 1: Consulting Swagger

A good thing about JHipster is, that it not only creates a Spring Boot application with the desired objects, but it also auto-generates a swagger documentation page about the API. This will help us find out how to handle the API.

Another good thing about JHipster is, that the REST API is secure by default in the sense that only logged in users will receive positive answers.

If we try to retrieve a blog (http://localhost:9000/api/blogs/4 in this case), we will get the answer, that we are not authorized:

{
    "timestamp": "2017-08-23T19:41:41.175+0000",
    "status": 401,
    "error": "Unauthorized",
    "message": "Access Denied",
    "path": "/api/blogs/4"
}

That is good in one sense, but we need to find out how to authenticate, before we can perform anything on the API. For that, we consult the swagger API documentation on http://localhost:/9000/#/docs. We find an entry for the user-jwt-controller and it tells us that we

According to the documentation, we need to POST a body of the format

{
  "password": "user",
  "rememberMe": true,
  "username": "user"
}

According to swagger, this has to be sent to the URL http://localhost:9060/api/authenticate. However, this did not work in my case. In my case, I had to send the POST to http://localhost:9000/api/authenticate instead. This might be a reason, why swagger’s Try it out! Button did not work.  I got an error telling me that there was no response from the server:

Also the curl command had to be reworked a little bit: the port had to be changed from 9060 to 9000 and the Backslashes had to be removed. But then it worked fine:

(anyhost)$ curl -X POST --header 'Content-Type: application/json' --header 'Accept: */*'  -d '{
   "password": "user",
   "rememberMe": true,
   "username": "user"
 }' 'http://localhost:9000/api/authenticate'
{
  "id_token" : "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyIiwiYXV0aCI6IlJPTEVfVVNFUiIsImV4cCI6MTUwNTkxOTg3NH0.n4B1RrmokbjuSQqWjOxNcUWgUXoqZODLb-nzEN-Km-7zjx7sGElWL8xgSWhc3DMrTHQDauT81ZZKF4IrmNh71A"
}

The curl command can be issued from the container or the docker host. Or it can be issued on the Vagrant host (a Windows 10 machine in my case), that is hosting the docker host VM, if port 9000 is mapped from Vagrant host to the VM:

In the latter case, we can use the graphical POSTman instead of a curl command, if we wish to:

Step 2: Create/Get Tokens

In the previous step, swagger has helped us to retrieve a token. Let us now write the result into an environment variable like follows:

(anyhost)$ USER_TOKEN=$(curl -k -D - -X POST --header 'Content-Type: application/json' --header 'Accept: */*' -d '{ "password": "user", "rememberMe": true, "username": "user" }' 'http://localhost:9000/api/authenticate' | grep id_token | awk -F '["]' '{print $4;}')
 % Total % Received % Xferd Average Speed Time Time Time Current
 Dload Upload Total Spent Left Speed
100 258 0 196 100 62 164 51 0:00:01 0:00:01 --:--:-- 164
(anyhost)$ echo $USER_TOKEN
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyIiwiYXV0aCI6IlJPTEVfVVNFUiIsImV4cCI6MTUwNjExMDU5NX0.MozFvp72L23PyAPsHg2tLfaxmqIRQXhT0DGrlRwAZDXISaceANqJIeOkaXbZXwDNPGW-3H_n3bzAwitvCeZE8g
Similarily, we can write the admin's token into a different environment variable:
(anyhost)$ ADMIN_TOKEN=$(curl -k -D - -X POST --header 'Content-Type: application/json' --header 'Accept: */*' -d '{ "password": "admin", "rememberMe": true, "username": "admin" }' 'http://localhost:9000/api/authenticate' | grep id_token | awk -F '["]' '{print $4;}')
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   276    0   212  100    64   1532    462 --:--:-- --:--:-- --:--:--  1536

Step 3: Create a Blog

Step 3.1: Explore createBlog on Swagger

Now let us use the token to create a blog. We consult swagger again. we find a createBlog function on

POST /api/blogs

with the example body

{
  "handle": "string",
  "id": 0,
  "name": "string",
  "user": {
    "activated": true,
    "email": "string",
    "firstName": "string",
    "id": 0,
    "imageUrl": "string",
    "langKey": "string",
    "lastName": "string",
    "login": "string",
    "resetDate": "2017-08-21T13:49:44.421Z"
  }
}

However, it does not make sense to specify all user parameters in the request. The user is not a blog private variable, it is only a pointer to an existing user. And the user is fully specified by the user ID (ID=4 for user named “user”). Therefore, let us try to use a minimized version as follows:

{
  "handle": "usersBlogCreatedByApi",
  "name": "user's blog created by API",
  "user": {
    "id": 4
  }
}

We also have omitted the blog id 0, since the ID will be assigned automatically.

Step 3.2: Find the user ID

How did I know that the user named “user” has the ID 4? I did not. I just issued the following curl command

(anyhost)$ curl -X GET --header 'Accept: */*' --header "Authorization: Bearer $USER_TOKEN" 'http://localhost:9000/api/users'

[ {
  "id" : 1,
  "login" : "system",
  "firstName" : "System",
  "lastName" : "System",
  "email" : "system@localhost",
  "imageUrl" : "",
  "activated" : true,
  "langKey" : "en",
  "createdBy" : "system",
  "createdDate" : "2017-08-15T11:47:06.180Z",
  "lastModifiedBy" : "system",
  "lastModifiedDate" : null,
  "authorities" : [ "ROLE_USER", "ROLE_ADMIN" ]
}, {
  "id" : 3,
  "login" : "admin",
  "firstName" : "Administrator",
  "lastName" : "Administrator",
  "email" : "admin@localhost",
  "imageUrl" : "",
  "activated" : true,
  "langKey" : "en",
  "createdBy" : "system",
  "createdDate" : "2017-08-15T11:47:06.180Z",
  "lastModifiedBy" : "system",
  "lastModifiedDate" : null,
  "authorities" : [ "ROLE_USER", "ROLE_ADMIN" ]
}, {
  "id" : 4,
  "login" : "user",
  "firstName" : "User",
  "lastName" : "User",
  "email" : "user@localhost",
  "imageUrl" : "",
  "activated" : true,
  "langKey" : "en",
  "createdBy" : "system",
  "createdDate" : "2017-08-15T11:47:06.180Z",
  "lastModifiedBy" : "system",
  "lastModifiedDate" : null,
  "authorities" : [ "ROLE_USER" ]
} ]

And there it is: the user named “user” has the ID=4.

Step 3.3: Create the Blog

Now let us create the blog:

(anyhost)$ curl -D - -X POST --header 'Content-Type: application/json' --header 'Accept: */*' --header "Authorization: Bearer $USER_TOKEN" -d '{
    "handle": "users_blog_via_api_using_user_token",
    "name": "user'\''s blog created by the API using the user'\''s token",
    "user": {
      "id": 4
    }
  }' 'http://localhost:9000/api/blogs'

HTTP/1.1 201 Created
x-powered-by: Express
x-blogapp-alert: blogApp.blog.created
expires: 0
cache-control: no-cache, no-store, max-age=0, must-revalidate
x-xss-protection: 1; mode=block
pragma: no-cache
location: /api/blogs/19
date: Wed, 23 Aug 2017 20:25:11 GMT
connection: close
x-content-type-options: nosniff
content-type: application/json;charset=UTF-8
x-application-context: blog:swagger,dev:8080
x-blogapp-params: 19
transfer-encoding: chunked

{
  "id" : 19,
  "name" : "user's blog created by the API using the user's token",
  "handle" : "users_blog_via_api_using_user_token",
  "user" : {
    "id" : 4,
    "login" : null,
    "firstName" : null,
    "lastName" : null,
    "email" : null,
    "activated" : false,
    "langKey" : null,
    "imageUrl" : null,
    "resetDate" : null
  }
}

Note that I have escaped the single quotes by ending the quote with a ', then write an escaped quote \' and start the quote again with a single '. Together a single quote within single quotes is escaped as '\''.

The blog can be seen on the JHipster UI on http://localhost:9000/#/blog:

Step 4: Update the Blog

Step 4.1: Consult Swagger

Before we delete the blog, let us update the blog. For that, let us consult swagger again:

The updateBlog function is similar to the createBlog function. However, this time we will not omit the blog ID, since this is the way to tell the API, which entity is to be updated. As with the createBlog function, we can omit all user’s variables apart from the user ID. This way, we can move the blog to the ownership of the admin, if we wish. Let us do that:

curl -D - -X PUT --header 'Content-Type: application/json' --header 'Accept: */*' --header "Authorization: Bearer $USER_TOKEN" -d '{ 
 "id": 19,
 "handle": "users_blog_via_api_using_user_token", 
 "name": "modified user'\''s blog created by the API using the user'\''s token and assigned to the admin now", 
 "user": { "id": 3 } 
}' 'http://localhost:9000/api/blogs'


HTTP/1.1 200 OK
x-powered-by: Express
x-blogapp-alert: blogApp.blog.updated
expires: 0
cache-control: no-cache, no-store, max-age=0, must-revalidate
x-xss-protection: 1; mode=block
pragma: no-cache
date: Wed, 23 Aug 2017 20:43:46 GMT
connection: close
x-content-type-options: nosniff
content-type: application/json;charset=UTF-8
x-application-context: blog:swagger,dev:8080
x-blogapp-params: 19
transfer-encoding: chunked

{
  "id" : 19,
  "name" : "modified user's blog created by the API using the user's token and assigned to the admin now",
  "handle" : "users_blog_via_api_using_user_token",
  "user" : {
    "id" : 3,
    "login" : "admin",
    "firstName" : "Administrator",
    "lastName" : "Administrator",
    "email" : "admin@localhost",
    "activated" : true,
    "langKey" : "en",
    "imageUrl" : "",
    "resetDate" : null
  }
}

With this request, we have assinged the blog to the admin and we have changed the name of the blog.

Note that you most probably need to change the blog ID of the request: use the ID you have received in the createBlog response.

Note that it is not possible to change only the name of the blog without specifying the handle or the user. The handle is mandatory also for PUT (Update) requests, and the user is cleared, if it is omitted.

Step 5: Delete the Blog

Now that we have created and updated a blog, let us delete the blog. We will see that the user can delete a blog that is owned by the admin. This is something we will improve later on.

Step 5.1: Consult Swagger

Again, let us have a look to Swagger on http://localhost:9000/#/docs:

Step 5.2: Delete Blog

For deletion of blog with ID=19, we just need to send a HTTP DELETE to /api/blogs/19:

(anyhost)$ curl -D - -X DELETE --header 'Content-Type: application/json' --header 'Accept: */*' --header "Authorization: Bearer $USER_TOKEN" 'http://localhost:9000/api/blogs/19'
HTTP/1.1 200 OK
x-powered-by: Express
x-blogapp-alert: blogApp.blog.deleted
expires: 0
cache-control: no-cache, no-store, max-age=0, must-revalidate
x-xss-protection: 1; mode=block
pragma: no-cache
date: Wed, 23 Aug 2017 20:53:21 GMT
connection: close
x-content-type-options: nosniff
content-length: 0
x-application-context: blog:swagger,dev:8080
x-blogapp-params: 19

We receive a 200 OK message. Yes, the blog was destroyed as expected, as can be verified on the UI again.

However, in a real-world scenario, the admin would be pissed, if a normal user was allowed to delete a blog owned by the admin, I guess. Let us improve the situation.

Step 6: Improving the API

Step 6.1: Improve the Answer when trying to delete an non-existing entity

Before improving the API, let us, see, the answer of the system, if we try to delete a blog that does not exist anymore:

(anyhost)$ curl -D - -X DELETE --header 'Content-Type: application/json' --header 'Accept: */*' --header "Authorization: Bearer $USER_TOKEN" 'http://localhost:9000/api/blogs/19'

HTTP/1.1 500 Internal Server Error
x-powered-by: Express
expires: 0
cache-control: no-cache, no-store, max-age=0, must-revalidate
x-xss-protection: 1; mode=block
pragma: no-cache
date: Mon, 21 Aug 2017 18:27:13 GMT
connection: close
x-content-type-options: nosniff
content-type: application/json;charset=UTF-8
x-application-context: blog:swagger,dev:8080
transfer-encoding: chunked

{
  "message" : "error.internalServerError",
  "description" : "Internal server error",
  "fieldErrors" : null
}

A HTTP 500 Server Error! Not good. It seems like the JHipster implementation is sub-optimal. We should receive a 404 Not Found Error instead.

A clue for the reason can be seen in the log of the server:

2017-08-21 18:27:13.426 DEBUG 4253 --- [  XNIO-2 task-8] org.jhipster.web.rest.BlogResource       : REST request to delete Blog : 9
Hibernate: select blog0_.id as id1_0_0_, blog0_.handle as handle2_0_0_, blog0_.name as name3_0_0_, blog0_.user_id as user_id4_0_0_, user1_.id as id1_6_1_, user1_.created_by as created_2_6_1_, user1_.created_date as created_3_6_1_, user1_.last_modified_by as last_mod4_6_1_, user1_.last_modified_date as last_mod5_6_1_, user1_.activated as activate6_6_1_, user1_.activation_key as activati7_6_1_, user1_.email as email8_6_1_, user1_.first_name as first_na9_6_1_, user1_.image_url as image_u10_6_1_, user1_.lang_key as lang_ke11_6_1_, user1_.last_name as last_na12_6_1_, user1_.login as login13_6_1_, user1_.password_hash as passwor14_6_1_, user1_.reset_date as reset_d15_6_1_, user1_.reset_key as reset_k16_6_1_ from blog blog0_ left outer join jhi_user user1_ on blog0_.user_id=user1_.id where blog0_.id=?
2017-08-21 18:27:13.431 ERROR 4253 --- [  XNIO-2 task-8] org.jhipster.aop.logging.LoggingAspect   : Exception in org.jhipster.web.rest.BlogResource.deleteBlog() with cause = 'NULL' and exception = 'No class org.jhipster.domain.Blog entity with id 9 exists!'

org.springframework.dao.EmptyResultDataAccessException: No class org.jhipster.domain.Blog entity with id 9 exists!

If we peek into org.jhipster.web.rest.BlogResource, the reason becomes clear:

@DeleteMapping("/blogs/{id}")
    @Timed
    public ResponseEntity deleteBlog(@PathVariable Long id) {
        log.debug("REST request to delete Blog : {}", id);
        blogRepository.delete(id); 
        return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();      
    }

The deleteBlog function calls blogRepository.delete(id) without checking, whether a blog with the corresponding id exists. Let us improve the situation:

    @DeleteMapping("/blogs/{id}")
    @Timed
    public ResponseEntity deleteBlog(@PathVariable Long id) {
        log.debug("REST request to delete Blog : {}", id);
        
        Blog blog = blogRepository.findOne(id);
      
        if(blog != null) { 
        	blogRepository.delete(id); 
        	return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
        } else {
        	return ResponseEntity.notFound().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
        }
        
        
    }

This has done the job:

(anyhost)$ curl -D - -X DELETE --header 'Content-Type: application/json' --header 'Accept: */*' --header "Authorization: Bearer $USER_TOKEN" 'http://localhost:90
00/api/blogs/9'

HTTP/1.1 404 Not Found
x-powered-by: Express
x-blogapp-alert: blogApp.blog.deleted
expires: 0
cache-control: no-cache, no-store, max-age=0, must-revalidate
x-xss-protection: 1; mode=block
pragma: no-cache
date: Mon, 21 Aug 2017 18:36:03 GMT
connection: close
x-content-type-options: nosniff
content-length: 0
x-application-context: blog:swagger,dev:8080
x-blogapp-params: 9

Let us update swagger correspondingly:

Step 6.2: Make sure only Owner can DELETE an blog object

In the moment, every user that is logged in can delete a blog. Let us make sure only the user of the blog can delete the object.

For that, let us try the following:

    @DeleteMapping("/blogs/{id}")
    @Timed
    public ResponseEntity deleteBlog(@PathVariable Long id) {
        log.debug("REST request to delete Blog : {}", id);
        
        Blog blog = blogRepository.findOne(id);
        
        
        if(blog != null) {
        	if (!blog.getUser().getLogin().equals(SecurityUtils.getCurrentUserLogin())) { 
        		// The user is not allowed to delete this blog, if it is not owned by this user:
            	        log.debug("Found blog, but user is not allowed to delete it");
            	        return ResponseEntity.status(403).headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
        	} else {
	        	blogRepository.delete(id); 
	        	return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
        	}
        } else {
        	return ResponseEntity.notFound().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
        }
        
        
    }

If we now create a blog for user “admin”:

$ curl -D - -X POST --header 'Content-Type: application/json' --header 'Accept: */*' --header "Authorization: Bearer $ADMIN_TOKEN" -d '{
   "id": 9, "handle": "users_blog_via_api_using_admin_token",
   "name": "modified user'\''s blog created by the API using the admin'\''s token",
   "user": {
     "id": 3
   }
 }' 'http://localhost:9000/api/blogs'

HTTP/1.1 200 OK
x-powered-by: Express
x-blogapp-alert: blogApp.blog.updated
expires: 0
cache-control: no-cache, no-store, max-age=0, must-revalidate
x-xss-protection: 1; mode=block
pragma: no-cache
date: Mon, 21 Aug 2017 19:30:40 GMT
connection: close
x-content-type-options: nosniff
content-type: application/json;charset=UTF-8
x-application-context: blog:swagger,dev:8080
x-blogapp-params: 9
transfer-encoding: chunked

{
  "id" : 18,
  "name" : "modified user's blog created by the API using the admin's token",
  "handle" : "users_blog_via_api_using_admin_token",
  "user" : {
    "id" : 3,
    "login" : "admin",
    "firstName" : "Administrator",
    "lastName" : "Administrator",
    "email" : "admin@localhost",
    "activated" : true,
    "langKey" : "en",
    "imageUrl" : "",
    "resetDate" : null
  }
}

Now we try to delete it with as user “user”:

$ curl -D - -X DELETE --header 'Content-Type: application/json' --header 'Accept: */*' --header "Authorization: Bearer $USER_TOKEN" 'http://localhost:90
00/api/blogs/18'

HTTP/1.1 403 Forbidden
x-powered-by: Express
x-blogapp-alert: blogApp.blog.deleted
expires: 0
cache-control: no-cache, no-store, max-age=0, must-revalidate
x-xss-protection: 1; mode=block
pragma: no-cache
date: Mon, 21 Aug 2017 19:33:20 GMT
connection: close
x-content-type-options: nosniff
content-length: 0
x-application-context: blog:swagger,dev:8080
x-blogapp-params: 18

That works as expected.

Now let us delete it as user admin:

$ curl -D - -X DELETE --header 'Content-Type: application/json' --header 'Accept: */*' --header "Authorization: Bearer $ADMIN_TOKEN" 'http://localhost:9
000/api/blogs/18'

HTTP/1.1 200 OK
x-powered-by: Express
x-blogapp-alert: blogApp.blog.deleted
expires: 0
cache-control: no-cache, no-store, max-age=0, must-revalidate
x-xss-protection: 1; mode=block
pragma: no-cache
date: Mon, 21 Aug 2017 19:39:34 GMT
connection: close
x-content-type-options: nosniff
content-length: 0
x-application-context: blog:swagger,dev:8080
x-blogapp-params: 18

Perfect, this is doing the job.

Excellent! Thump up!

 

Summary

We have explored the API of a Spring Boot application created by JHipster. We have seen how we can use Swagger to find out, how to use the API. We have created, updated and deleted an object. After seeing that the JHipster implementation of the API is sub-optimal, we have improved the API:

  • we have made sure the API does not throw a HTTP 500 Server Error, if someone tries to delete a non-existing entity. Instead, we will return a 404 Not Found Error.
  • We have show, how we can make sure that only the owner can delete an entity.

Next Steps

  • We have (not yet) applied the improvements on other functions like create (automatically assign the object to the logged in user as owner), update.
  • In the moment, only logged-in users can read the entities. We could show, how to make an entity visible to the world instead, i.e. how to allow anonymous access.

 

 

 

0

Hello Java Hipster: Angular 4 and Spring Boot


In this blog post, Java Hipster will help us creating a mini blog application based on Angular 4 and Spring Boot. Angular is a popular framework for creating reactive single page applications, while Spring Boot is a robust java-based backend framework that helps you create database access and RESTful APIs.

We will closely follow the JHipster introduction on YouTube and explore administrative functions like user management, logging and API management (with swagger). After creating an importing a simple model for a blog, we will manipulate the blog and its entries via the GUI.

For your convenience, we will run everything in a Docker container, so you do not need to install any of the required software packages (apart from Docker itself). As a little goody, we will outline how to add some additional security functions: we will restrict delete access to authorized users. That is an extension to the many good examples shown in the JHipster introduction on YouTube video.

Tools and Versions used

  • Vagrant 1.8.6
  • Virtualbox 5.0.20 r106931
  • jhipster Docker image v4.6.2

Why JHipster?

JHipster is a Yeoman-based code generator for a complete Web application based on Angular and Spring Boot, two popular frameworks for front-end and backend systems.

  • Angular is a popular single page frontend framework
  • Spring Boot is a high-performance backend framework

mixed with CSS Bootstrap styles, and an easy-to use web-based modeling tool that creates text-based models that can be used to generate the corresponding database and REST entities/functions, JHipster, JHipster is a quick way to get projects started.

I have not checked out yet all other features mentioned on their home page, like

  • microservice support with JHipster Registry, Netflix OSS, Elastic stack and Docker

We will build our first application within a Docker container.

Step 0: Install 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”).

Prerequisites of this step:

  • I recommend to have direct access to the Internet: via Firewall, but without HTTP proxy. However, if you cannot get rid of your HTTP proxy, read this blog post.
  • Administration rights on you computer.

Steps to install a Docker Host VirtualBox VM:

Download and install Virtualbox (if the installation fails with error message “Oracle VM Virtualbox x.x.x Setup Wizard ended prematurely” see Appendix A of this blog post: Virtualbox Installation Workaround below)

1. Download and Install Vagrant (requires a reboot)

2. Download Vagrant Box containing an Ubuntu-based Docker Host and create a VirtualBox VM like follows:

(basesystem)# mkdir ubuntu-trusty64-docker ; cd ubuntu-trusty64-docker
(basesystem)# vagrant init williamyeh/ubuntu-trusty64-docker
(basesystem)# vagrant up
(basesystem)# vagrant ssh

Now you are logged into the Docker host and we are ready for the next step: to download the Docker image and to start the application in a container.

Step 1: Start JHipster Docker Container

(dockerhost)$ docker run -it -p 8080:8080 -p 3001:3001 -p 9000:9000 -p 9060:9060 -v $(pwd):/localdir jhipster/jhipster:v4.6.2 bash
(containter)# cd /localdir

Note: You also can omit the version tag (:v4.6.2) and work with the latest image. However, v4.6.2 is the version tested in this blog post.

Note that you might have to view (docker ps) and stop (docker stop) any container that is running on port 8080 first, since I have experienced problems, when mapping the port 8080 to another port than 8080 (I need to try again). If you have followed step 0, you will need to issue the command

(dockerhost) sudo docker stop cadvisor

Step 2: Create, Start and Login to App

Step 2.1: Create App

(container)# mkdir blog; cd blog
(container)# yo jhipster

I have chosen

  • monolithic
  • base name: blog
  • default java package name: org.jhipster <– should use another name, probably
  • authentication: JWT
  • database type: SQL
  • production database: MySQL
  • development database: H2 with disk-based persistence
  • Hibernate 2nd level cache: Yes, with ehcache (local cache, for a single node)
  • Other Technologies (choose none, just press enter)
  • Framework: Angular 2
  • libSass: Yes
  • Internationalization: Yes
  • Chosen English and German
  • Testing Frameworks (bBesides JUnit and Karma:
    • Gatling
    • Protractor

Wait for ~2-4 minutes

Step 2.2 Start App

In order to be able to explore an (empty) JHipster app, we can start the application as follows:

Start Spring Boot Application:

In the project root directory, type:

(container)# ./mvnw

Only as a reference: Start Webpack development server (was not needed in my case!)

container)# yarn start

Step 2.3 (optional): Connect to (default) App

In a local browser, connect to localhost:8080:

Sign in as admin with password “admin”.

Step 2.4 (optional): Explore (default) App

The Entities are empty yet, but unser Administration we can see the user management, metrics etc:

We have not yet created and imported a model, so this is still the default JHipster app. It comes with a User Model, though, with three users created per default:

  • admin / admin
  • user / user
  • system / system?

Under Administration -> Metrics, we can see JVM Metrics as well as HTTP statistics:

Under Administration -> Configuration, we see the Spring properties, even though they seem to be read-only:

However, the log configuration can be edited in the way that a log can be chosen e.g. to show DEBUG instead of WARN messages only

Under Administration -> API we find a swagger page that helps you to test your REST services:

Okay, I am cheating a little here: you will not be able to see the blog-resource yet, since we will create it not before the next two steps. You will only see the account, the profile info, the user-jwt controller and the user resource. It can be used to test the interface. E.g. you can read all users by clicking the button

Try it out!

of users-resource GET /api/users and we will receive something like follows:

[
  {
    "id": 1,
    "login": "system",
    "firstName": "System",
    "lastName": "System",
    "email": "system@localhost",
    "imageUrl": "",
    "activated": true,
    "langKey": "en",
    "createdBy": "system",
    "createdDate": "2017-08-15T11:47:06.180Z",
    "lastModifiedBy": "system",
    "lastModifiedDate": null,
    "authorities": [
      "ROLE_USER",
      "ROLE_ADMIN"
    ]
  },
  {
    "id": 3,
    "login": "admin",
    "firstName": "Administrator",
    "lastName": "Administrator",
    "email": "admin@localhost",
    "imageUrl": "",
    "activated": true,
    "langKey": "en",
    "createdBy": "system",
    "createdDate": "2017-08-15T11:47:06.180Z",
    "lastModifiedBy": "system",
    "lastModifiedDate": null,
    "authorities": [
      "ROLE_USER",
      "ROLE_ADMIN"
    ]
  },
  {
    "id": 4,
    "login": "user",
    "firstName": "User",
    "lastName": "User",
    "email": "user@localhost",
    "imageUrl": "",
    "activated": true,
    "langKey": "en",
    "createdBy": "system",
    "createdDate": "2017-08-15T11:47:06.180Z",
    "lastModifiedBy": "system",
    "lastModifiedDate": null,
    "authorities": [
      "ROLE_USER"
    ]
  }
]

We also can choose the German language, since we have chosen English AND German:

Step 3: Create and Download Your Model

You can create and download the model from JDL Studio and access it from your application. If you have followed the instructions above, you need to make sure the file is visible from the JHipster container. For that, you need to place it to the folder you have mapped in step 1 by using the -v docker run flag.

It might be more convenient to cut&paste the content into a file /localdir/blog/model.jh within the container:

entity Blog {
name String required minlength(3),
handle String required minlength(2)
}

entity Entry {
title String required,
content TextBlob required,
date ZonedDateTime required
}

entity Tag {
name String required minlength(2)
}

relationship ManyToOne {
Blog{user(login)} to User,
Entry{blog(name)} to Blog
}

relationship ManyToMany {
Entry{tag(name)} to Tag{entry}
}

paginate Entry, Tag with infinite-scroll

The graphical representation of the model looks like follows:

The modes is re-using the User model that is present in JHipster per default. Now, each blog is mapped to a user. Each blog can have many blog entries and each entry can have many tags, while each tag can be attached to many entries.

In the next step, we will import this model.

Step 4: Import Model

To import the model, we stop the currently running mvnh process (if so), and import the model as follows:

(container)# yo jhispter:import-jdl ./model.jh
Overwrite liquibase/master.xml?: a

Step 5: Start Spring Boot App

After 5 minutes or so, we can start the application again:

(container)# ./mvnw

Note: yarn start alone will not do the trick. See https://stackoverflow.com/questions/43291477/jhipster-cannot-login-after-starting-with-yarn-start-webpack

Step 6: Connect and Login as admin

In a Browser, we need to navigate to localhost:8080 and login as admin with password admin or user with password user. It does not make a difference for now.

We will notice, that now the model entities are available:

Step 7: Create a Blog

Let us choose “Blog” and Create a new Blog:

Click Save.

Step 8: Create a Blog Entry

Now let us Create a new Entry on Entities -> Entry:

We can see that the Blog field is not mandatory (otherwise the mouse will show that the Save button cannot be pressed). However, let us choose the Admin’s Blog we have just created:

Note, that I have put only part of the content in headline style.

 

The ID is 2 instead of 1, since I have played around already and I have created and deleted a blog entry already.

Step 9: Add Multi User Support

In this step we will make sure that a user can only edit and delete any entity that belongs to the logged in user.

Step 9.1: Log out and log in as User “user”

We now log out and log in as user “user” with password “user”. You will notice that you can see and edit or delete any entity that belongs to the admin. This is a no-go for any multi-user application and needs to be fixed.

Step 9.2: Add a User’s Blog

Let us first create a User’s Blog and a Blog entity like follows:

We will see both Blog entries:

(since you have not yet fixed the HTML Display, the content will look different in your case, you will see the escaped HTML instead like

However the Date is missing. Oups, when checking with my screenshot above, I have entered a wrong date far in the future. And there is a bad side-effect: when I try to edit or delete the entry, the application does not react.

For the records:

  • we need to make sure that the user input is verified
  • we need to find a way to delete this entry

For now, let us create a new entry:

This is an entry that can be edited and deleted, if needed.

Note: It seems to take quite long, until the entry is loaded. Also, it will be “Loading…” forever, if you click on View of the upper entry and use the “back” button on that page. Maybe this is the case because of the bad entry #2?

If you are reloading the page, it does display, though and the new entry can be edited, if needed:

Step 9.3: Test write access to admin’s entries

Now click Edit of the admin’s entry and you will see that the user is allowed to edit admin’s entries.

We need to take two measures:

  • the Edit and Delete buttons should not be visible (or grayed out and not functional) on entries that do not belong to to the logged in user.
  • the user should not be able to circumvent security by directly calling the Edit or Delete function for entities of other users.

Step 9.4: Start Application in develop Mode

In order to view the results of any file changes immediately, you need to issue the command

(container)# yarn start

and connect to port 9000 instead of 8080:

Log in as user “user” again.

Step 9.5: Remove foreign Blogs from Blog Table View

For now, as user named “user” we can see both blogs we had created:

A quick&dirty way to remove the Edit and Delete buttons from the user’s view is to remove the admin’s blogs from the view, as shown in the JHipster introduction on YouTube:

Let us search for the function getAllBlogs in the java File BlogResource (found on the container as src/main/java/org/jhipster/web/rest/BlogResource.java)

and we change

findAll()

by

findByUserIsCurrentUser()

which is available on JHipster on any model per default.

After restarting ./mvnw, the admin’s blog will be removed from the blog table:

Step 9.6: Restrict Access for Single Blog View

In the previous step, we have removed the admin’s blog from the user’s blog table view. However, the admins’s blog can still be accessed by the user named “user”, if he knows (or guesses) the ID:

This is a topic that is not mentioned in the JHipster introduction on YouTube. So, let that fix too:

On java/org/jhipster/repository/BlogRepository.java, we can reset the return value to “null”, if the found blog does not belong to the logged in user. For that we add:

// file: java/org/jhipster/repository/BlogRepository.java
import import org.jhipster.security.SecurityUtils;
...
    @GetMapping("/blogs/{id}")
    @Timed
    public ResponseEntity getBlog(@PathVariable Long id) {
        log.debug("REST request to get Blog : {}", id);
        Blog blog = blogRepository.findOne(id);
        
        // The user is not allowed to access this blog, if it is not owned by this user:
        if (!blog.getUser().getLogin().equals(SecurityUtils.getCurrentUserLogin())) { blog = null; }

        return ResponseUtil.wrapOrNotFound(Optional.ofNullable(blog));
    }

For that we import the SecurityUtils and compare the user login of the found blog with the user login of the user that is logged in. If it is not equal, we just reset the found value to null.

With that, the blog is not accessible anymore, after we have restarted ./mvnw:

Excellent! Thump up!

This is, what we want to achieve: blog/3 is not visible because it is not owned by the logged in user.

However, let us double-check, that general access to the blogs is not broken. For that, let us access the user’s blog, /blog/4 in my case:

Yes, that’s it.

Note: since we have not done anything on the DELETE function, any user, who knows how to access the API, will still be able to DELETE or UPDATE the entry. This will be covered in the next blog post, where we will have a closer look to the API created by Spring Boot. To cut is short, following code change (in red) will be shown there:

    @DeleteMapping("/blogs/{id}")
    @Timed
    public ResponseEntity deleteBlog(@PathVariable Long id) {
        log.debug("REST request to delete Blog : {}", id);
        
        Blog blog = blogRepository.findOne(id);     
        
        if(blog != null) {
        	if (!blog.getUser().getLogin().equals(SecurityUtils.getCurrentUserLogin())) { 
        		// The user is not allowed to delete this blog, if it is not owned by this user:
            	        log.debug("Found blog, but user is not allowed to delete it");
            	        return ResponseEntity.status(403).headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
        	} else {
	        	blogRepository.delete(id); 
	        	return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
        	}
        } else {
        	return ResponseEntity.notFound().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
        }
        
        
    }

Note: A similar code change is needed for the PUT (Update) function.

Step 10: Fix the HTML Display

We can see on Entities -> Entry -> View that the HTML display is not correct, since it is escaped:

Let us fix that now. For that, navigate to src/main/webapp/app/entities/entry/entry-detail.component.html (you can do that inside the container via vi, or, since the content is mapped to the Docker host, we also can use an IDE like Visual Studio Code from outside of the container:


There, we can change

<span>{{entry.content}}</span>

by

<span [innerHTML]="entry.content">Loading...</span>

Unlike what is shown in the JHipster introduction, the application running on localhost:8080 does not seem to recognize, when a file is changed. Even stopping and restarting mvnw did not change anything:

Instead, I have run

(container)# yarn start

within the container and I have connected to the Webpack in development mode on port 9000:

This did the trick.

Excellent! Thump up!

Now, I can change the files from within the container (by opening an extra session into the Docker container via docker exec -it <containerid> bash, and webpack will re-transpile the code within seconds (but I need to refresh the Browser, it seems).

Note: after running the application some days on my notebook, I have logged in as admin again (without reloading the application) and the change now was visible on port 8080 as well:

I guess, this problem needs some more investigation…

 

Summary

In this blog post, we have created a little blog application using the Java Hipster code generator. For that, we have

  • installed a Docker host, if needed
  • started a Docker JHipster container on the Docker host
  • run and explored the default administration functions of the JHipster application like
    • user management,
    • API exploration (via swagger) and
    • logging
  • edited a model for our application using the JDL Studio Web Page
  • imported the model file of a blog application (blogs, entries, tags) into JHipster
  • explored the new functions of the application
  • tweaked the Spring Boot Read and Delete function, so that only the owner is able to see and delete a blog (this is an addition to what you will find on the JHipster introduction on YouTube.)
  • tweaked the view of the blog within an Angular template to display HTML content correctly

We have seen, how easy it is to import an arbitrary model into the default JHipster application and to create and display the entities defined in the model. In our case, we have created a simple blog application with blogs, entries and tags.

Coming Soon

  • I already have started a Blog Post, where I am exploring the REST API that is automatically generated by JHipster. We will learn, how to use swagger to find the correct curl commands, how to authenticate the service and how to tweak the REST interface, so  only the owner of a blog entity is allowed to delete the entry. Follow this blog, if you are interested in the blog post.
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.