Web Development with Bootstrap 4 and Angular 2 - Second Edition

3.2 (9 reviews total)
By Sergey Akopkokhyants , Stephen Radford
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Saying Hello!

About this book

Two of the most popular frontend frameworks, Angular and Bootstrap, have undergone a major overhaul to embrace emerging web technologies so that developers can build cutting-edge web applications.

Inside this title you'll dive, fingers first, into the basics of both the tools, and once you're familiar with them, you'll move onto Bootstrap's new grid system and Angular's built-in directives. You'll then learn how to format output using Angular's pipes and how to make use of the built-in router to set up routes for all your components.

Webpack will be your buddy to wrap up your project. Then, after throwing in some SASS to make things pretty, you'll learn how to validate the forms you've built and debug your application. Finally, you'll go on to learn how to obtain smooth transitioning from Bootstrap to Angular and then how to hook up with a server and use Firebase as the persistence layer.

Once you're done with this book, you'll not only have a lovely little e-commerce application running, but you'll also take with you the confidence to innovate and build your own applications with ease.

Publication date:
November 2016
Publisher
Packt
Pages
404
ISBN
9781785880810

 

Chapter 1. Saying Hello!

Let's follow several steps to establish a development environment for the simplest application possible, to show you how easy it is to get a web application up and running with Angular 2 and Bootstrap 4. At the end of the chapter, you will have a solid understanding of:

  • How to set up your development environment
  • How TypeScript can change your development life
  • Core concepts of Angular and Bootstrap
  • How to create a simple Angular component with Bootstrap
  • How to display some data through it
 

Setting up a development environment


Let's set up your development environment. This process is one of the most overlooked and often frustrating parts of learning to program because developers don't want to think about it. Developers must know the nuances of how to install and configure many different programs before they start real development. Everyone's computers are different; as a result, the same setup may not work on your computer. We will expose and eliminate all of these problems by defining the various pieces of environment you need to set up.

Defining a shell

The shell is a required part of your software development environment. We will use the shell to install software and run commands to build and start the web server to bring life to your web project. If your computer has the Linux operating system installed then you will use a shell called Terminal. There are many Linux-based distributions out there that use diverse desktop environments, but most of them use the equivalent keyboard shortcut to open Terminal.

Note

Use keyboard shortcut Ctrl + Alt + T to open Terminal in Ubuntu, Kali, and Linux Mint. If it doesn't work for you, please check the documentation for your version of Linux.

If you have a Mac computer with OS X installed, then you will use the Terminal shell as well.

Note

Use keyboard shortcut command + space to open the Spotlight, type Terminal to search and run.

If you have a computer with a Windows operating system installed, you can use the standard Command Prompt, but we can do better. In a minute I will show you how can you install Git on your computer, and you will have Git Bash free.

Note

You can open a Terminal with the Git Bash shell program on Windows.

I will use the Bash shell for all exercises in this book whenever I need to work in Terminal.

Installing Node.js

Node.js is technology we will use as a cross-platform runtime environment to run server-side web applications. It is a combination of a native, platform-independent runtime based on Google's V8 JavaScript engine and a huge number of modules written in JavaScript. Node.js ships with different connectors and libraries help you use HTTP, TLS, compression, file system access, raw TCP and UDP, and more. You as a developer can write your own modules on JavaScript and run them inside the Node.js engine. The Node.js runtime makes it easy to build a network event-driven application servers.

Note

The terms package and library are synonymous in JavaScript so we will use them interchangeably.

Node.js is utilizing the JavaScript Object Notation (JSON) format widely in data exchanges between the server and client sides because it is readily expressed in several parse diagrams, notably without the complexities of XML, SOAP, and other data exchange formats.

You can use Node.js for the development of service-oriented applications, doing something different than web servers. One of the most popular service-oriented applications is node package manager (npm), which we will use to manage library dependencies, deployment systems, and which underlies the many platform-as-a-service (PaaS) providers for Node.js.

If you do not have Node.js installed on your computer, you should download the pre-build installer from https://nodejs.org/en/download, or you can use the unofficial package managers from https://nodejs.org/en/download/package-manager. You can start to use Node.js immediately after installation. Open Terminal and type:

node --version

Node.js will respond with the version number of the installed runtime:

v4.4.3

Bear in mind that the version of Node.js installed on my computer could be different from yours. If these commands give you a version number, you are ready to go with Node.js development.

Setting up npm

The npm is a package manager for JavaScript. You can use it to find, share, and reuse packages of code from many developers across the world. The number of packages dramatically grows every day and now is more than 250K. The npm is a Node.js package manager and utilizes it to run itself. The npm is included in the setup bundle of Node.js and available just after installation. Open Terminal and type:

npm --version

The npm must respond on your command with a version number:

2.15.1

My Node.js comes with that particular version of npm. The npm gets updated quite frequently, so you'll want to move to the latest version with the following command:

npm install [email protected] -g

You may experience permission problems to search or install packages with npm. If that is the case, I recommend following the instructions from https://docs.npmjs.com/getting-started/fixing-npm-permissions and don't use superuser privileges to fix them.

The following command gives us information about Node.js and the npm install:

npm config list

There are two ways to install npm packages: locally or globally. In cases when you would like to use the package as a tool, it's better install it globally:

npm install -g <package_name>

If you need to find the folder with globally installed packages you can use the next command:

npm config get prefix

Installation global packages are important, but best to avoid if not needed. Mostly you will install packages locally.

npm i <package_name>

You may find locally installed packages in the node_modules folder of your project.

Installing Git

If you're not familiar with Git then you're really missing out! Git is a distributed version control system and each Git working directory is a full-fledged repository. It keeps a complete history of changes and has full version tracking capabilities. Each repository is entirely independent of network access or a central server. You can keep Git repositories on your computer and share it with your mates, or you can take advantage of the many online VCS providers. The big guys you should look at closely are GitHub, Bitbucket, and Gitlab.com. Each has its own benefits, depending on your needs and project type.

Mac computers comes with Git already installed into the operating system but usually the version of Git is not the same as the latest one. You can update or install Git on your computer via a set of pre-build installers available on the official website https://git-scm.com/downloads. After installation, you can open Terminal and type:

git -version

Git must respond with a version number:

git version 2.8.1.windows.1

As I said, for developers who use computers with an installed Windows operation system, you now have Git Bash free on your system.

Code editor

You can imagine how many programs for code editing exist, but we will talk today only about the free, open source, runs everywhere Visual Studio Code from Microsoft. You can use any program you prefer for development, but I will be using only Visual Studio code in our future exercises, so please install it from http://code.visualstudio.com/Download.

 

A TypeScript crash course


TypeScript is an open source programming language developed and maintained by Microsoft. Its initial public release was in October 2012 and was presented by Anders Hejlsberg, the lead architect of C# and creator of Delphi and Turbo Pascal.

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Any existing JavaScript is also valid TypeScript. It gives you type checking, explicit interfaces, and easier module exports. For now, it includes ES5, ES2015, ES2016, and, in fact, it's a little like getting some of tomorrow's ECMAScripts early so that we can play with some of those features today.

Here is the relationship between ECMAScripts and TypeScript:

Writing code using TypeScript is relatively straightforward if you already have a background in the JavaScript language. Try the TypeScript playground http://www.typescriptlang.org/play to play with IntelliSense, find references, and so on, directly from your browser.

Types

TypeScript provides a static type checking operation that allows many bugs in the development cycle to be caught early. TypeScript enables type checking at compile time via type annotations. Types in TypeScript are always optional, so you can ignore them if you prefer the regular dynamic typing of JavaScript. It supports number, boolean, and string type annotations for primitive types and any for dynamically-typed structures. In the following example, I added type annotations to return and parameters for function:

function add(first: number, second: number): number { 
  return first + second; 
} 

In one moment of compilation, a TypeScript compiler can generate a declaration file which contains only signatures of the exported types. The resulting declaration file with the extension .d.ts along with a JavaScript library or module can be consumed later by a third-party developer. You can find a vast collection of declaration files for many popular JavaScript libraries at:

Arrow function

Functions in JavaScript are first class citizens, which means they can be passed around like any other values:

var result = [1, 2, 3] 
  .reduce(function (total, current) {   
    return total + current; 
  }, 0); // 6 

The first parameter in reduce is an anonymous function. Anonymous functions are very useful in many scenarios but too verbose. TypeScript introduced new, less verbose syntax to define anonymous functions called arrow function syntax:

var result = [1, 2, 3] 
  .reduce( (total, current) => {   
    return total + current; 
  }, 0); // 6 

Or event less:

var result = [1, 2, 3] 
  .reduce( (total, current) => total + current, 0); // 6 

When defining parameters, you can even omit parentheses if the parameters are just a single identifier. So the regular map method of array:

var result = [1, 2, 3].map(function (x) {  
  return x * x  
}); 

Could be much more concise:

var result = [1, 2, 3].map(x => x * x); 

Both syntaxes (x) => x * x and x => x * x are allowed.

Another important feature of arrow function is that it doesn't shadow this and pick it up from the lexical scope. Let's assume we have a constructor function Counter which increments the value of an internal variable age in timeout and prints it out:

function Counter() { 
  this.age = 30; 
  setTimeout(() => { 
    this.age += 1; 
    console.log(this.age); 
  }, 100); 
} 
new Counter(); // 31 

As result of using the arrow function, the age from the scope of Counter is available inside the callback function of setTimeout. Here is the converted to JavaScript ECMAScript 5 code:

function Counter() { 
    var _this = this; 
    this.age = 30; 
    setTimeout(function () { 
        _this.age += 1; 
        console.log(_this.age); 
    }, 100); 
} 

The following variables are all lexical inside arrow functions:

  • arguments
  • super
  • this
  • new.target

Block scope variables

All variables in ES5 declared with a var statement are function-scoped, and their scope belongs to enclosing functions. The result of the following code can be confusing because it returns undefined:

var x = 3; 
function random(randomize) { 
    if (randomize) { 
        // x initialized as reference on function 
        var x = Math.random();  
        return x; 
    } 
    return x; // x is not defined 
} 
random(false); // undefined 

The x is an inner variable of the random function and does not have any relation to the variable defined on the first line. The result of calling the random function at the last line returned undefined, because the JavaScript interprets the code in random function like that:

function random(randomize) { 
    var x; // x is undefined 
    if (randomize) { 
        // x initialized as reference on function 
        x = Math.random(); 
        return x; 
    } 
    return x; // x is not defined 
} 

This confusing code can be fixed in TypeScript with new block-scope variable declarations:

  • The let is a block-scope version of var
  • The const is similar let but allows initialize variable only once

The TypeScript compiler throws more errors with new block-scope variable declarations and prevents writing complicated and damaged code. Let's change var to let in the previous example:

let x = 3; 
function random(randomize) { 
    if (randomize) { 
        let x = Math.random(); 
        return x; 
    } 
    return x; 
} 
random(false); // 3 

And now our code works as expected.

Note

I recommend using const and let to make the code cleaner and safer.

Template literals

If we need string interpolation, we usually combine the values of variables and string fragments such as:

let out: string = '(' + x + ', ' + y + ')'; 

The TypeScript supports template literals--string literals allowing embedded expressions. You can use the string interpolation features of the template literals out of the box:

let out: string = `(${x}, ${y})`; 

If you need multiline string, the template literals can help again:

Let x = 1, y = 2; 
let out: string = ` 
Coordinates 
 x: ${x},  
 y: ${y}`; 
console.log(out); 

The last line prints results as follow:

Coordinates 
 x: 1,  
 y: 2 

Note

I recommend using template literals as a safer way of string interpolation.

The for-of loop

We usually use for statement or forEach method of Array to iterate over elements in JavaScript ES5:

let arr = [1, 2, 3]; 
// The for statement usage 
for (let i = 0; i < arr.length; i++) { 
    let element = arr[i]; 
    console.log(element); 
} 
// The usage of forEach method 
arr.forEach(element => console.log(element)); 

Each of these methods has its benefit:

  • We can interrupt the for statement via break orcontinue
  • The forEach method is less verbose

The TypeScript has for-of loop as a combination of both of them:

const arr = [1, 2, 3]; 
for (const element of arr) { 
    console.log(element); 
} 

The for-of loop supports break and continue and can use the index and value of each array via new Array method entries:

const arr = [1, 2, 3]; 
for (const [index, element] of arr.entries()) { 
    console.log(`${index}: ${element}`); 
} 

Default value, optional and rest parameters

We quite often need to check the input parameters of functions and assign default values to them:

function square(x, y) { 
  x = x || 0; 
  y = y || 0; 
  return x * y; 
} 
let result = square(4, 5); // Out 20 

The TypeScript has syntax to handle default values of parameters to make previous functions shorter and safer:

function square(x: number = 0, y: number = 0) { 
  return x * y; 
} 
let result = square(4, 5); // Out 20 

Note

A default value of a parameter is assigned only by its undefined value.

Each parameter of a function in JavaScript ES5 is optional, so an omitted one equals undefined. To make it strict, TypeScript expects a question mark at the end of parameters we want to be optional. We can mark the last parameter of the square function as optional and call the function with one or two parameters:

function square(x: number = 0, y?: number) { 
 if (y) { 
   return x * y; 
 } else { 
  return x * x; 
 } 
} 
let result = square(4); // Out 16 
let result = square(4, 5); // Out 20 

Note

Any optional parameters must follow the required parameters.

In some scenarios, we need to work with multiple parameters as a group, or we may not know how many parameters a function takes. The JavaScript ES5 provides the arguments variable in the scope of functions to work with them. In TypeScript, we can use a formal variable to keep the rest of the parameters. The compiler builds an array of the arguments passed in with the name given after the ellipses so that we can use it in our function:

function print(name: number, ...restOfName: number[]) { 
  return name + " " + restOfName.join(" "); 
} 
let name = print("Joseph", "Samuel", "Lucas"); 
// Out: Joseph Samuel Lucas 

Interfaces

The interface is the way of defining contracts inside and outside the code of your project. We use the interface in TypeScript only to describe a type and the shape of data to help us keep our code error-free. In comparison with many other languages, the TypeScript compiler doesn't generate any code for the interface so that it has not runtime cost. The TypeScript defines interfaces via the interface keyword. Let's define a type Greetable:

interface Greetable { 
  greetings(message: string): void; 
} 

It has a member function called greetings that takes a string argument. Here is how we can use it as a type of parameter:

function hello(greeter: Greetable) { 
  greeter.greetings('Hi there'); 
} 

Classes

JavaScript has a prototype-based, object-oriented programming model. We can instantiate objects using the object literal syntax or constructor function. Its prototype-based inheritance is implemented on prototype chains. If you come from an object-oriented approach, you may feel uncomfortable when you try to create classes and inheritance based on prototypes. TypeScript allows for writing code based on an object-oriented class-based approach. The compiler translates the class down to JavaScript and works across all major web browsers and platforms. Here is the class Greeter. It has a property called greeting, a constructor, and a method greet:

class Greeter { 
  greeting: string; 
  constructor(message: string) { 
    this.greeting = message; 
  } 
  greet() { 
    return "Hello, " + this.greeting; 
  } 
} 

To refer to any member of the class we prepend this. To create an instance of the class we use the new keyword:

let greeter = new Greeter("world"); 

We can extend an existing class to create new ones via inheritance:

class EmailGreeter extends Greeter {  
  private email: string;  
  constructor(emailAddr: string, message: string) {  
    super(message); 
    this.email = emailAddr;  
  }  
  mailto() {  
    return "mailto:${this.email}?subject=${this.greet()}";  
  }  
} 

In the class EmailGreeter, we demonstrate several features of inheritance in TypeScript:

  • We use extends to create a subclass
  • We must call super in the first line of the constructor to pass values into base class
  • We call the greet method of the base class to create a subject for mailto

The TypeScript classes support public, protected, and private modifiers to access the members that we declared throughout our programs. Each member of the class is public by default. There are not a requirement to labeled all public members with that keyword but you may mark them explicitly. Use protected modifier if you need to restrict access to members of the class from outside, but bear in mind that they are still available from deriving classes. You can mark the constructor as protected so that we cannot instantiate the class but we can extend it. The private modifier restricts access to member only on the class level.

If you look at constructors of EmailGreeter, we had to declare a private member email and a constructor parameter emailAddr. Instead, we can use parameter properties to let us create and initialize a member in one place:

class EmailGreeter extends Greeter { 
  constructor(private email: string, message: string) { 
    super(message); 
  } 
  mailto() { 
    return "mailto:${this.email}?subject=${this.greet()}"; 
  } 
} 

You can use any modifier in parameter properties.

Note

Use parameter properties to consolidate the declaration and assignment in one place.

TypeScript supports getters and setters to organize intercepting access to members of an object. We can change the original Greeter class with the following code:

class Greeter {  
  private _greeting: string; 
  get greeting(): string { 
    return this._greeting; 
  } 
  set greeting(value: string) { 
    this._greeting = value || ""; 
  } 
  constructor(message: string) { 
    this.greeting = message; 
  } 
  greet() { 
    return "Hello, " + this.greeting; 
  } 
} 

We check the value parameter inside the setter of greeting and modify it if necessary to empty string before assigning it to the private member.

TypeScript supports class members via the static modifier as well. Here the class Types contains only static members:

class Types {  
  static GENERIC: string = ""; 
  static EMAIL: string = "email"; 
} 

We can access those values through prepending the name of the class:

console.log(Types.GENERIC); 

TypeScript gives us supreme flexibility via abstract classes. We cannot create instances of them, but we can use them to organize base classes from which each distinct class may be derived. We can convert the greeting class into abstract with just one keyword:

abstract class BaseGreeter {  
  private _greeting: string; 
  get greeting(): string { 
    return this._greeting; 
  } 
  set greeting(value: string) { 
    this._greeting = value || ""; 
  } 
  abstract greet(); 
} 

The method greet is marked as abstract. It doesn't contain an implementation and must be implemented in derived classes.

Modules

When we are writing the code, we usually divide it into functions and the blocks inside those functions. The size of a program can increase very quickly, and individual functions start to blend into the background. We can make such a program more readable if we split them into large units of an organization like modules. At the beginning of writing a program, you may not know how to structure it, and you can use structureless principles. When your code becomes stable you can put pieces of functionality into separate modules to make them easy to track, update, and share. We store modules of TypeScript in files, exactly one module per file and one file per module.

The JavaScript ES5 doesn't have built-in support for modules and we used AMD or CommonJS syntax to work with them. TypeScript supports the concept of modules.

How do the scope and module depend on each other? The global scope of JavaScript doesn't have access to the scope of executing modules. It creates its own scope for each individual execution module, so everything declared inside the module is not visible from outside. We need to explicitly export them to make them visible and import them to consume them. The relationship between modules is defined at the file level regarding exports and imports. Any file defines a top-level export or import and is considered a module. Here is a string-validator.ts file which contains the exported declaration:

export interface StringValidator { 
  isAcceptable(s: string): boolean; 
} 

I have created another file zip-validator.ts with several members, but exported only one of them to hide another one from outside:

const numberRegexp = /^[0-9]+$/; 
export class ZipCodeValidator implements StringValidator { 
  isAcceptable(s: string) { 
    return s.length === 5 && numberRegexp.test(s); 
  } 
} 

You can re-export declarations if your module extends other modules. Here validators.ts contains a module, wraps other validator modules, and combines all their exports in one place:

export * from "./string-validator"; 
export * from "./zip-validator"; 

Now we can import validator modules using one of the import forms. Here is a single export from a module:

import { StringValidator } from "./validators";  
let strValidator = new StringValidator(); 

To prevent a naming conflict we can rename an imported declaration:

import { ZipCodeValidator as ZCV } from "./validators"; 
let zipValidator = new ZCV(); 

Finally, we can import an entire module into a single variable, and use it to access module exports:

import * as validator from "./validators";  
let strValidator = new validator.StringValidator(); 
let zipValidator = new validator.ZipCodeValidator(); 

Generics

The authors of TypeScript put maximal effort into helping us to write reusable code. One of the tools that helps us to create code that can work with a variety of types rather than a single one is generics. The benefits of generics include:

  • Allows you to write code/use methods which are type-safe. An Array<string> is guaranteed to be an array of strings.
  • The compiler can perform a compile-time check on code for type safety. Any attempt to assign the number into an array of strings causes an error.
  • Faster than using any type to avoid casting into a required reference type.
  • Allows you to write code which is applicable to many types with the same underlying behavior.

Here is the class I have created to show you how useful generics can be:

class Box<T> { 
    private _value : T;  
    set value(val : T) {  
        this._value = val;  
    }  
    get value() : T {  
        return this._value;  
    }  
} 

This class keeps the single value of a particular type. To set or return it we can use corresponding getter and setter methods:

var box1 = new Box<string>();  
box1.setValue("Hello World");  
console.log(box1.getValue());  
var box2 = new Box<number>();  
box2.setValue(1);  
console.log(box2.getValue()); 
var box3 = new Box<boolean>();  
box3.setValue(true);  
console.log(box3.getValue()); 
// Out: Hello World 
// Out: 1 
// Out: true 
 

What are promises?


A promise represents the final result of an asynchronous operation. There are a number of libraries that support the use of promises in TypeScript. But before starting to talk about this, let's talk a bit about the browser environment which executes your JavaScript code.

Event loop

Each browser tab has an event loop and uses different tasks to coordinate events, user interactions, running scripts, rendering, networking, and so on. It has one or more queues to keep an ordered list of tasks. Other processes run around the event loop and communicate with it by adding tasks to its queue such as:

  • The timer waits after a given period and then adds a task to the queue
  • We can call a requestAnimationFrame function to coordinate DOM updates
  • DOM elements can call event handlers
  • The browser can request the parsing of an HTML page
  • JavaScript can load an external program and perform computation on it

Many of the items in the list above are JavaScript code. They are usually small enough, but if we run any long-running computation it could block execution of other tasks, and as a result it freezes the user interface. To avoid blocking the event loop we can:

  • Use the web worker API to execute a long-running computation in a different process of the browser
  • Do not wait for the result of a long-running computation synchronously and allow the task to inform us about results via events or callbacks asynchronously

Asynchronous results via events

The following code uses an event-driven approach to convince us and adds event listeners to execute small code snippets inside:

var request = new XMLHttpRequest(); 
request.open('GET', url); 
 
request.onload = () => { 
    if (req.status == 200) { 
        processData(request.response); 
    } else { 
        console.log('ERROR', request.statusText); 
    } 
}; 
 
request.onerror = () => { 
    console.log('Network Error'); 
}; 
 
request.send(); // Add request to task queue 

The method send in the last line of code just adds another task to the queue. This approach is useful if you receive results multiple times, but this code is quite verbose for a single result.

Asynchronous results via callbacks

To manage asynchronous results via callbacks, we need to pass a callback function as a parameter into asynchronous function calls:

readFileFunctional('myfile.txt', { encoding: 'utf8' }, 
    (text) => { // success 
        console.log(text); 
    }, 
    (error) => { // failure 
        // ... 
    } 
); 

This approach is very easy to understand, but it has its disadvantages:

  • It mixes up input and output parameters
  • It is complicated to handle errors especially in the code combined many callbacks
  • It is more complicated to return result from combined asynchronous functions

Asynchronous results via promises

As I mentioned earlier, the promise represents the final result of an asynchronous operation happening in the future. Promises have the following advantages:

  • You write cleaner code without callback parameters
  • You do not adapt the code of the underlying architecture for delivery results
  • Your code handles errors with ease

A promise may be in one of the following states:

  • Pending state: The asynchronous operation hasn't completed yet
  • Resolved state: The asynchronous operation has completed and the promise has a value
  • Rejected state: The asynchronous operation failed and the promise has a reason which indicates why it failed

The promise becomes immutable after resolving or rejecting.

Usually, you write the code to return the promise from functions or methods:

function readFile(filename, encode){ 
  return new Promise((resolve, reject) => { 
    fs.readFile(filename, enccode, (error, result) => { 
      if (error) { 
        reject(error); 
      } else { 
        resolve(result); 
      } 
    }); 
  }); 
} 

We use the new keyword with a function constructor to create the promise. We add a factory function with two parameters into the constructor, which does the actual work. Both parameters are callback functions. Once the operation has successfully completed the factory function calls the first callback with the result. If the operation fails it calls the second function with the reason.

The returned promise has several methods such as .then and .catch to inform us of the result of the execution so that we can act accordingly:

function readJSON(filename){
 return readFile(filename, 'utf8').then((result) => {
 console.log(result);
 }, (error) => {
 console.log(error);
 });
} 

We can call another operation returns promise to quickly transform the result of original one:

function readJSON(filename){ 
  return readFile(filename, 'utf8').then((result) => { 
    return JSON.parse(result); 
  }, (error) => { 
    console.log(error); 
  } 
} 
 

Angular 2 concepts


The Angular 2 is a development platform for building web, mobile, and desktop applications. It is based on web standards to make web development simpler and more efficient, and entirely different from the Angular JS 1.x. The architecture of Angular 2 builds on top of the web component standard so that we can define custom HTML selectors and program behavior for them. The Angular team develops Angular 2 to use in the ECMAScript 2015, TypeScript, and Dart languages.

Building blocks of Angular 2

Any web application built on Angular 2 consist of:

  • HTML templates with Angular-specific markup
  • Directives and components managing the HTML templates
  • Services containing application logic
  • Special bootstrap function which helps to load and start the Angular application

Module

The Angular 2 application is an assembly of many modules. Angular 2 itself is a set of modules with names beginning with the @angular prefix, combined into libraries:

  • The @angular/core is the primary Angular 2 library and contains all core public APIs
  • The @angular/common is the library which restricts APIs to reusable components, directives, and form building
  • The @angular/router is the library that supports navigation
  • The @angular/http is the library that helps us work asynchronously via HTTP

Metadata

Metadata is information we can attach to underlying definitions via TypeScript decorators to tell Angular how to modify them. Decorators play a significant role in Angular 2.

Directives

Directives are the fundamental building block of Angular 2 and allows you to connect behavior to an element in the DOM. There are three kinds of directive:

  • Attribute directives
  • Structural directives
  • Components

A directive is a class with an assigned @Directive decorator.

Attribute directives

The attribute directive usually changes the appearance or behavior of an element. We can change several styles, or use it to render text bold or italic by binding it to a property.

Structural directives

The structural directive changes the DOM layout by adding and removing other elements.

Component

The component is a directive with a template. Every component is made up of two parts:

  • The class, where we define the application logic
  • The view, which is controlled by the component and interacts with it through an API of properties and methods

A component is a class with the assigned @Component decorator.

Template

The component uses the template to render the view. It is regular HTML with custom defined selectors and Angular-specific markups.

Data binding

The Angular 2 supports data binding to update parts of the template via the properties or methods of a component. The binding markup is part of data binding; we use it on the template to connect both sides.

Service

Angular 2 has no definition of a service. Any value, function, or feature can be a service, but usually it is a class created for a distinct purpose with an assigned @Injectable decorator.

Dependency injection

Dependency injection is a design pattern that helps configure objects by an external entity and resolve dependencies between them. All elements in the loosely coupled system know little or nothing about definitions of each other. We can replace almost any element with alternative implementation without breaking the whole system.

 

SystemJS loader and JSPM package manager


We have discussed TypeScript modules, so it's time to talk about tools we can use for loading modules in our scripts.

SystemJS Loader

SystemJS is a universal dynamic module loader. It hosts the source code on GitHub at the following address https://github.com/systemjs/systemjs. It can load modules in the web browser and Node.js in the following formats:

  • ECMAScript 2015 (ES6) or TypeScript
  • AMD
  • CommonJS
  • Global scripts

SystemJS loads modules with an exact circle reference, binding support, and assets through the module naming system such as CSS, JSON, or images. Developers can easily extend the functionality of the loader via plugins.

We can add SystemJS loader to our future project:

  • Via direct link to a Content Delivery Network (CDN)
  • By installing via npm manager

In both scenarios, we include a reference to the SystemJS library in our code and configure it via the config method:

<!DOCTYPE html> 
<html> 
  <head> 
    <script src="https://jspm.io/system.js"></script>   
    <script src="https://jspm.io/system.js"></script>

    <script>
      System.config({
      packages: {
      './': {
      defaultExtension: false
            }
         }
      });
    </script>

    <script>
    System.import('./app.js');
    </script>
  </head> 
  <body> 
    <div id="main"></div> 
  </body> 
</html> 

We will speak about installation via npm manager a bit later in this chapter.

JSPM package manager

The developers of the SystemJS followed the single-responsibility principle and implemented a loader for doing only one thing: loading the modules. To make modules available in your project, we need to use the package manager. We spoke about the npm package manager at the beginning, so now we will talk about the JSPM package manager sitting on top of SystemJS. It can:

  • Download modules from any registry such as npm and GitHub
  • Compile modules into simple, layered, and self-executing bundles with a single command

The JSPM package manager looks like an npm package manager, but it puts the browser loader first. It helps you organize a seamless workflow for installing and using libraries in the browser with minimum effort.

 

Writing your first application


Now, when we have everything in place, it's time to create our first project, which is actually an npm module. Open Terminal and create the folder hello-world. I intentionally follow the npm package naming conventions:

  • The package name length should be greater than zero and cannot exceed 214
  • All the characters in the package name must be lowercase
  • The package name can consist of/include hyphens
  • The package name must contain any URL-safe characters (since the name ends up being part of a URL)
  • The package name should not start with dot or underscore letters
  • The package name should not contain any leading or trailing spaces
  • The package name cannot be the same as a node.js/io.js core module or a reserved/blacklisted name like http, stream, node_modules, and so on.

Move the folder in and run the command:

npm init

npm will ask you several questions to create a package.json file. This file keeps important information about your package in JSON format:

  • Project information like name, version, author, and license
  • Set of packages the project depends on
  • Set of pre-configured commands to build and test the project

Here is how package.js could look:

{ 
  "name": "hello-world", 
  "version": "1.0.0", 
  "description": "The Hello World", 
  "author": " Put Your Name Here", 
  "license": "MIT" 
  "scripts": { 
    "test": "echo "Error: no test specified" && exit 1" 
  } 
} 

We are ready to configure our project.

TypeScript compile configuration

Run the Visual Studio code and open the project folder. We need to create a configuration file which guides the TypeScript compiler on where to find the source folder and required libraries and how to compile the project. From the File menu create tsconfig.json file, and copy/paste the following:

{ 
  "compilerOptions": { 
    "target": "es5", 
    "module": "commonjs", 
    "moduleResolution": "node", 
    "sourceMap": true, 
    "emitDecoratorMetadata": true, 
    "experimentalDecorators": true, 
    "removeComments": false, 
    "noImplicitAny": false 
  }, 
  "exclude": [ 
    "node_modules", 
    "typings/main", 
    "typings/main.d.ts" 
  ] 
} 

Let's look closer at the compilerOptions:

  • The target option specifies the ECMAScript version such es3, es5, or es6.
  • The module option specifies the module code generator from one of these: none, commojs, amd, system, umd, es6, or es2015.
  • The moduleResolution option determines how modules get resolved. Use node for Node.js/io.js style resolution or classic.
  • The sourceMap flag tells the compiler to generate a corresponding map file.
  • The emitDecoratorMetadata emits the design-type metadata for decorated declarations in source.
  • The experimentalDecorator enables experimental support for ES7 decorators such iterators, generators and array comprehensions.
  • The removeComments removes all comments except copyright header comments beginning with /*!.
  • The noImplicitAny raises an error on expressions and declarations with an implied any type.

You can find the full list of compiler options here: https://www.typescriptlang.org/docs/handbook/compiler-options.html.

The TypeScript compiler needs type definition files of JavaScript libraries from node_modules of our project because it doesn't recognize them natively. We help it with typings.json file. You should create the file and copy/paste the following:

{
   "ambientDependencies": {
     "es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654"
   }
} 

We should provide enough information to typings tool to get any typings file:

  • The registry dt is located in the DefinitelyTyped source. This value could be npm, git
  • The package name in DefinitelyTyped source is the es6-shim
  • We are looking for the version 0.31.2 updated 2016.03.17 12:06:54

Task automation and dependency resolution

Now, it's time to add the libraries into the package.json file that the application requires. Please update it accordingly:

{ 
  "name": "hello-world", 
  "version": "1.0.0", 
  "description": "The Hello World", 
  "author": "Put Your Name Here", 
  "license": "MIT", 
  "scripts": { 
    "start": "tsc && concurrently "npm run tsc:w" "npm run lite" ", 
    "lite": "lite-server", 
    "postinstall": "typings install", 
    "tsc": "tsc", 
    "tsc:w": "tsc -w", 
    "typings": "typings" 
  }, 
  "dependencies": { 
    "@angular/common":  "~2.0.1", 
    "@angular/compiler":  "~2.0.1", 
    "@angular/core":  "~2.0.1", 
    "@angular/http":  "~2.0.1", 
    "@angular/platform-browser":  "~2.0.1", 
    "@angular/platform-browser-dynamic":  "~2.0.1", 
    "@angular/router":  "~3.0.1", 
    "@angular/upgrade": "~2.0.1", 
 
    "systemjs": "0.19.39", 
    "core-js": "^2.4.1", 
    "reflect-metadata": "^0.1.8", 
    "rxjs": "5.0.0-beta.12", 
    "zone.js": "^0.6.25", 
 
    "angular-in-memory-web-api": "~0.1.1", 
    "bootstrap": "4.0.0-alpha.4" 
  }, 
  "devDependencies": { 
    "concurrently": "^3.0.0", 
    "lite-server": "^2.2.2", 
    "typescript": "^2.0.3", 
    "typings":"^1.4.0" 
  } 
} 

Our configuration includes scripts to handle common development tasks such:

  • The postinstall script runs after the package is installed
  • The start script runs by the npm start command
  • The arbitrary scripts lite, tsc, tsc:w, and typings are executed by the npm run <script>.

You can find more documentation on the following web page: https://docs.npmjs.com/misc/scripts.

After finishing the configuration let's run npm manager to install the packages required. Go back to Terminal and enter the following command:

npm i

During installation, you may see warning messages in red starting with:

npm WARN

You should ignore them if the installation finishes successfully. After installation, the npm executes the postinstall script to run typings installation.

 

Creating and bootstrapping an Angular component


The Angular 2 application must always have a top-level component, where all other components and logic lie. Let's create it. Go to the Visual Studio code and create a sub-folder app of the root directory where we will keep the source code. Create the file app.component.ts under app folder, and copy/paste the following:

// Import the decorator class for Component 
import { Component } from '@angular/core'; 
 
@Component({ 
  selector: 'my-app', 
  template: '<h1> Hello, World</h1>' 
}) 
export class AppComponent { } 

As you can see, we have added metadata via @Component decorator to the class AppComponent. This decorator tells Angular how to process the class via configuration with the following options:

  • The selector defines the name of an HTML tag which our component will link
  • We pass in any service in the providers property. Any service registered here becomes available to this component and its children
  • We give away any number of style files to styles a particular component
  • The template property will hold the template of the component
  • The template url is a URL to an external file containing a template for the view

We need to export the class AppComponent to make it visible from other modules and Angular can instantiate it.

The Angular application is a composition of multiple modules marked with NgModule decorator. Any application must have at least one root module, so let's create AppModule in the app.module.ts file:

import { NgModule } from '@angular/core'; 
import { BrowserModule } from '@angular/platform-browser'; 
 
@NgModule({ 
  imports: [ BrowserModule ] 
}) 
export class AppModule { } 

The WebBrowser is a collection of modules and providers specific for web browsers such as document DomRootRenderer, and so on. We import WebBrowser into the application module to make all of those providers and modules available in our application, thereby reducing the amount of boilerplate code-writing required. Angular contains the ServerModule: a similar module for the server side.

Now we need to start up our application. Let's create main.ts file under app folder, and copy/paste the following:

import { platformBrowserDynamic } from  
     '@angular/platform-browser-dynamic'; 
 
import { AppModule } from './app.module'; 
 
const platform = platformBrowserDynamic(); 
 
platform.bootstrapModule(AppModule); 

Last but not least, we rely on the bootstrap function to load top-level components. We import it from '@angular/platform-browser-dynamic'. Angular has a different kind of bootstrap function for:

  • Web workers
  • Development on mobile devices
  • Rendering the first page of an application on a server

Angular does several tasks after instantiation of any component:

  • It creates a shadow DOM for it
  • It loads the selected template into the shadow DOM
  • It creates all the injectable objects configured with 'providers' and 'viewProviders'

In the end, Angular 2 evaluates all template expressions and statements against the component instance.

Now, create index.html file in Microsoft Visual Studio code under the root folder with the following content:

<html> 
  <head> 
    <title>Angular 2 First Project</title> 
    <meta charset="UTF-8"> 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
    <link rel="stylesheet" href="styles.css"> 
 
    <!-- 1. Load libraries --> 
     <!-- Polyfill(s) for older browsers --> 
    <script src="node_modules/core-js/client/shim.min.js"> 
    </script> 
     
    <script src="node_modules/zone.js/dist/zone.js"></script> 
    <script src="node_modules/reflect-metadata/Reflect.js"> 
    </script> 
    <script src="node_modules/systemjs/dist/system.src.js"> 
</script> 
 
    <!-- 2. Configure SystemJS --> 
    <script src="systemjs.config.js"></script> 
    <script> 
      System.import('app') 
      .catch(function(err){ console.error(err);  }); 
    </script> 
  </head> 
 
  <!-- 3. Display the application --> 
  <body> 
    <my-app>Loading...</my-app> 
  </body> 
</html> 

Because we are referencing the systemjs.config.js file, let's create it in the root folder with the code:

(function (global) { 
  System.config({ 
    paths: { 
      // paths serve as alias 
      'npm:': 'node_modules/' 
    }, 
    // map tells the System loader where to look for things 
    map: { 
      // our app is within the app folder 
      app: 'app', 
      // angular bundles 
      '@angular/core': 'npm:@angular/core/bundles/core.umd.js', 
      '@angular/common': 'npm:@angular/common/bundles/common.umd.js', 
      '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js', 
      '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js', 
      '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', 
      '@angular/http': 'npm:@angular/http/bundles/http.umd.js', 
      '@angular/router': 'npm:@angular/router/bundles/router.umd.js', 
      '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js', 
      // other libraries 
      'rxjs':                      'npm:rxjs', 
      'angular-in-memory-web-api': 'npm:angular-in-memory-web-api', 
    }, 
    // packages tells the System loader how to load when no filename and/or no extension 
    packages: { 
      app: { 
        main: './main.js', 
        defaultExtension: 'js' 
      }, 
      rxjs: { 
        defaultExtension: 'js' 
      }, 
      'angular-in-memory-web-api': { 
        main: './index.js', 
        defaultExtension: 'js' 
      } 
    } 
  }); 
})(this); 

Compiling and running

We are ready to run our first application. Go back to Terminal and type:

npm start

This script runs two parallel Node.js processes:

  • The TypeScript compiler in watch mode
  • The static lite-server loads index.html and refreshes the browser when the application file changes

In your browser you should see the following:

Tip

You can find the source code in the chapter_1/1.hello-world folder.

Adding user input

We now need to include our text input and also, specify the model we want to use. When a user types in the text input, our application shows the changed value in the title. Also, we should import the FormsModule into the AppModule:

import { NgModule }      from '@angular/core'; 
import { BrowserModule } from '@angular/platform-browser'; 
import { FormsModule }   from '@angular/forms'; 
 
import { AppComponent }   from './app.component'; 
 
@NgModule({ 
  imports:      [ BrowserModule, FormsModule ], 
  declarations: [ AppComponent ], 
  bootstrap:    [ AppComponent ] 
 
}) 
export class AppModule { } 

Here is the updated version of app.component.ts:

import { Component } from '@angular/core'; 
 
@Component({ 
  selector: 'my-app', 
  template: ` 
<h1>Hello, {{name || 'World'}}</h1>  
<input type="text" [(ngModel)]="name" placeholder="name"> 
`}) 
export class AppComponent {  
  name: string = 'World'; 
} 

The ngModel attribute declares a model binding on that element, and anything we type into the input box will be automatically bound to it by Angular. Obviously, this isn't going to be displayed on our page by magic; we need to tell the framework where we want it echoed. To show our model on the page, we just need to wrap the name of it in double curly braces:

{{name}}

I popped this in place of World in our <h1> tag and refreshed the page in my browser. If you pop your name in the input field, you'll notice that it's automatically displayed in your heading in real time. Angular does all of this for us, and we haven't written a single line of code:

Now, while that's great, it would be nice if we could have a default in place so it doesn't look broken before a user has entered their name. What's awesome is that everything in between those curly braces is parsed as an Angular expression, so we can check and see if the model has a value, and if not, it can echo 'World'. Angular calls this an expression and it's just a case of adding two pipe symbols as we would in TypeScript:

{{name || 'World'}}  

It's good to remember that this is TypeScript, and that's why we need to include the quotation marks here, to let it know that this is a string and not the name of a model. Remove them and you'll notice that Angular displays nothing again. That's because both the name and World models are undefined.

Tip

You can find the source code into the chapter_1/2.hello-input. folder.

 

Integrating Bootstrap 4


Now that we've created our Hello World application, and everything is working as expected, it's time to get involved with Bootstrap and add a bit of style and structure to our app. At the time of writing this book Bootstrap 4 was in alpha version, so bear in mind that the code and markup of your application might be slightly different. We need to add the Bootstrap 4 style sheet into the index.html file:

<meta name="viewport" content="width=device-width, initial-scale=1"> 
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css"> 
<link rel="stylesheet" href="styles.css"> 

The application is currently misaligned to the left, and everything is looking cramped, so let's sort that out first with a bit of scaffolding. Bootstrap comes with a great mobile first responsive grid system that we can utilize with the inclusion of a few div elements and classes. First, though, let's get a container around our content to clean it up immediately:

Note

Mobile first is a way of designing/developing for the smallest screens first and adding to the design rather than taking elements away.

<div class="container">  
  <h1>Hello, {{name || 'World'}}</h1>  
  <input type="text" [(ngModel)]="name">  
</div> 

If you resize your browser window, you should start to notice some of the responsiveness of the framework coming through and see it collapsing:

Now, I think it's a good idea to wrap this in what Bootstrap calls a Jumbotron (in previous versions of Bootstrap this was a hero unit). It'll make our headline stand out a lot more. We can do this by wrapping our H1 and input tags in a new div with the jumbotron class:

<div class="container"> 
  <div class="jumbotron"> 
    <h1>Hello, {{name || 'World'}}</h1> 
   <input type="text" ng-model="name"> 
  </div> 
</div> 

It's starting to look a lot better, but I'm not too happy about our content touching the top of the browser like that. We can make it look a lot nicer with a page header, but that input field still looks out of place to me.

First, let's sort out that page header:

<div class="container"> 
  <div class="page-header"> 
    <h2>Chapter 1 <small>Hello, World</small></h2> 
  </div> 
  <div class="jumbotron"> 
    <h1>Hello, {{name || 'World'}}</h1> 
    <input type="text" [(ng-model)]="name"> 
  </div> 
</div> 

I've included the chapter number and title here. The <small> tag within our <h2> tag gives us a nice differentiation between the chapter number and the title. The page-header class itself just gives us some additional margin and padding as well as a subtle border along the bottom.

The utmost thing I think we could improve upon is that input box. Bootstrap comes with some cool input styles so let's include those. First, we need to add the class of form-control to the text input. This will set the width to 100% and also bring out some beautiful styling such as rounded corners and glowing when we focus on the element:

<input type="text" [(ngModel)]="name" class="form-control"> 

Much better, but to me it looks a little small when you compare it with the heading. Bootstrap provides two additional classes we can include that will either make the element smaller or larger: form-control-lg and form-control-sm respectively. In our case, the form-control-lg class is the one we want, so go ahead and add that to the input.

<input type="text" [(ngModel)]="name"  
       class="form-control form-control-lg"> 

Tip

You can find the source code in the chapter_1/3.hello-bootstrap.

 

Summary


Our app's looking great and working exactly how it should, so let's recap what we've learnt in the first chapter.

To begin with, we saw just how to setup a working environment and finish the TypeScript crash course.

The Hello World app we've created, while being very basic, demonstrates some of Angular's core features:

  • Component directives
  • Application bootstrapping
  • Two-way data binding

All of this was possible without writing a single line of TypeScript, as the component we created was just to demonstrate two-way data binding.

With Bootstrap, we utilized a few of the many available components such as the Jumbotron and the page-header classes to give our application some style and substance. We also saw the framework's new mobile first responsive design in action without cluttering up our markup with unnecessary classes or elements.

In Chapter 2, Working with Bootstrap Components, we're going to explore more Bootstrap fundamentals and introduce the project we're going to be building over the course of this book.

About the Authors

  • Sergey Akopkokhyants

    Sergey Akopkokhyants is a software architect with more than 20 years of professional experience in designing and developing client and server-side applications. He is also a certified Java developer and project manager. He has general knowledge of many tools, languages, and platforms. For the last decade, Sergey has been responsible for customizing and producing weboriented applications for wholesale business management solutions projects for several worldwide mobile communication companies. His responsibilities have included: architecture design and guidance of client software development using Flex, CSS, HTML, JavaScript, TypeScript, and Dart, and client-server integration with Java. He is also the founder and an active contributor to several open source projects on GitHub. Sergey is passionate about web design and development and likes sharing his expertise with others, helping them to increase their skills and productivity. He is author of the book Mastering Dart and also he was one of reviewers of the books Learning Dart and Dart Cookbook.

    Browse publications by this author
  • Stephen Radford

    Stephen Radford is a full-stack web and app developer based in the heart of England--Leicester. Originally from Bristol, Stephen moved to Leicester after studying Graphic Design at college to accept a job at one of the UK’s largest online marketing companies.

    Whilst working at a number of agencies, Stephen developed several side projects, including FTPloy, a SaaS designed to make continuous deployment available to everyone. The project was subsequently a finalist in the .NET Awards Side Project of the Year category.

    He and his business partner now run Cocoon, a web development company that builds and maintains web and mobile apps. Cocoon also works closely with a handful of startups and businesses to develop ideas into websites and apps.

    Browse publications by this author

Latest Reviews

(9 reviews total)
45 days later and i am still waiting for my books without any news from the customer services, even though I have already sent an email.
the book is ok. the examples are dated for A4 and B4.
Excellent source of information and exceptional prices. Great seller!!!
Book Title
Access this book, plus 7,500 other titles for FREE
Access now