Author Archives: admin

Angular2 – Typescript

Introduction

TypeScript is a superset to JavaScript, which means, that it compiles into pure JavaScript in the end.

Why do we use it then? First, it provides ‘strong typing’ (that’s where the name comes from). This means that we can (and should) assign types to our variables and class members. These types won’t compile to JavaScript (as JS does not know types) but we will get compilation errors if we assign wrong types or make any other type-related errors. This is a HUGE help in the daily development work and should not be underestimated!

Second, TypeScript introduces some nice features, JS does not have out of the box (at least in the ES5 specification). This includes classes (‘class’ keyword), interfaces, generics and modules. Being able to use these constructs makes our code cleaner, easier to read and helps us avoid nasty errors. Especially in combination with the strong typing we are really able to write high quality code and track down errors quickly.

Where can I learn all the TypeScript fundamentals?

There are a lot of great resources out there which will get you started very quickly. The official documentation is not too bad to be honest, so you may give it a try: http://www.typescriptlang.org/Handbook

There’s also a course here on Udemy, though I have not tested it! https://www.udemy.com/typescript/

Can we mix TypeScript and JavaScript?

Yes, we can. No one is preventing us from not setting types, using ‘var’ instead of ‘let’ or using pure JavaScript libraries (i.e. libraries which don’t offer a TypeScript version/ implementation).

Can’t I use ‘normal’ JavaScript to write Angular 2 applications?

You can absolutely do that. But currently finding good documentation and examples on Angular 2 using plain JavaScript is extremely hard. And to be honest: TypeScript will be the standard ‘language’ to be used when developing Angular 2 applications. So I definitely recommend using TypeScript.

Using types

  • We can define type for variables. Example:

01-typing/typing.ts

// Declaring a variable with a type
// Using the 'let' keyword to create a variable ('const' would define an immutable constant)
let myString: string;
myString = 'This is a string';
  • If we try to assign a different type will get an error on the typescript compiler
// Declaring a variable with a type
// Using the 'let' keyword to create a variable ('const' would define an immutable constant)
let myString: string;
myString = 'This is a string';

// Try to assign a number to a string => Error
myString = 4;
> tsc typing
typing.ts(10,1): error TS2322: Type '4' is not assignable to type 'string'
  • Typescript can also infer types. So in this example will also get an error:
// TypeScript can also infer types
let anotherString = 'This is a string without :string'; // => Type 'string' was inferred from the assigned value

// This will still resolve in a compilation error
anotherString = 4;
  • WARNING: Infering the type doesnt work if we not assign a value in declaration
// TypeScript may only infer values when those values are assigned at the declaration
// This does not work:
let yetAnotherString;
yetAnotherString = 'This is a string';

// TypeScript does not know the type, therefore we don't get an error ... but no we're also ignoring TypeScripts strength: Typing
yetAnotherString = 5;
  • By default if we dont specify type in the declaration will get the type any.
let yetAnotherString: any;
  • Other basic types:
// Other basic types

let aString: string;
let aNumber: number;
let aBoolean: boolean;
let anArray: Array<string>; // This is a generic type => May only hold 'strings' in this case
let anything: any; // Any can be used if we don't know the actual type => Use it rarely!
// We also got void (=> nothing) and enums (a set of numeric values)
  • We can also create custom types.
  • Summary : Strong types your variables: prevent errors.

 

Classes

// Classes allow us to create 'blueprints' for objects
// In Angular 2 we use classes a lot. For example to create Components, Services, Directives, Pipes, ...

// How to create a class

class Car {
    engineName: string;
    gears: number;
    private speed: number;

    constructor(speed: number) {
        this.speed = speed || 0;
    }

    accelerate(): void {
        this.speed++;
    }

    throttle():void {
        this.speed--;
    }

    getSpeed():void {
        console.log(this.speed);
    }

    static numberOfWheels(): number {
        return 4;
    }
}

// Instantiate (create) an object from a class

let car = new Car(5);
car.accelerate();
car.getSpeed();

console.log(Car.numberOfWheels());

* In old Javascript we already got class but now with TS use of classes is easier.

  • We can define private properties accessible from outside. By default are public
  • Static methods for been called for the class not for the instance. Yo dont need to create an object to call it:
    console.log(Car.numberOfWheels());

 

Interfaces

// Interfaces allow us to create contracts other classes/ objects have to implement
// We can use them to define custom types without creating classes
// Interfaces ARE NOT compiled to JavaScript! It's just for checking/ validation done by our TypeScript compiler

// Example interface

interface User {
    username: string;
    password: string;
    confirmPassword?: string; // Optional property => Does not have to be implemented
}

let user:User;

// This value does not satisfy the interface => Compilation error
// user = { anything: 'anything', anynumber: 5};

// This value does satisfy the interface
user = {username: 'max', password: 'supersecret'};

// Interfaces can also contain functions (without the function body - as it only is a blueprint/ requirement)

interface CanDrive {
    accelerate(speed:number): void;
}

let car:CanDrive = {
    accelerate: function (speed:number) {
        // ...
    }
};

Generics

  • Generic types allow us to be flexible with typing.
// Generics are types which can hold/ use several types
// We're only touching the very basics here - you can go MUCH more into detail

// Consider the Array object

let numberArray: Array<number>; // This array will only accept numbers

// Try to initialize it with strings

// numberArray = ['test']; // => Error
numberArray = [1,2,3];

 

 

Wrap up & modules

// TypeScript is modular, we can divide our code up over several files
// In Angular 2 we then use  "import {} from ''" to access the code in these files

// We export a class, interface, variable, ... by adding 'export' keyword in front of it

export class ExportedClass {
    // This class is exported
}

Deep dive into TypeScript

How to learn TypeScript

Throughout this course we’re always using TypeScript and I am convinced that you’ll be able to learn it ‘on the fly’. But a little head start is never wrong.

What is TypeScript?

TypeScript is a superset to JavaScript, which means, that it compiles into pure JavaScript in the end. Why do we use it then?

First, it provides ‘strong typing’ (that’s where the name comes from). This means that we can (and should) assign types to our variables and class members. These types won’t compile to JavaScript (as JS does not know types) but we will get compilation errors if we assign wrong types or make any other type-related errors. This is a HUGE help in the daily development work and should not be underestimated!

Second, TypeScript introduces some nice features, JS does not have out of the box (at least in the ES5 specification). This includes classes (‘class’ keyword), interfaces, generics and modules. Being able to use these constructs makes our code cleaner, easier to read and helps us avoid nasty errors. Especially in combination with the strong typing we are really able to write high quality code and track down errors quickly.

Where can I learn all the TypeScript fundamentals?

There are a lot of great resources out there which will get you started very quickly.

The official documentation is not too bad to be honest, so you may give it a try: http://www.typescriptlang.org/Handbook

There’s also a course here on Udemy, though I have not tested it! https://www.udemy.com/typescript/

Can we mix TypeScript and JavaScript?

Yes, we can. No one is preventing us from not setting types, using ‘var’ instead of ‘let’ or using pure JavaScript libraries (i.e. libraries which don’t offer a TypeScript version/ implementation).

Can’t I use ‘normal’ JavaScript to write Angular 2 applications?

You can absolutely do that. But currently finding good documentation and examples on Angular 2 using plain JavaScript is extremely hard. And to be honest: TypeScript will be the standard ‘language’ to be used when developing Angular 2 applications. So I definitely recommend using TypeScript

 

 

Angular2 – Custom project & Workflow setup

An alternative to create the project with the Angular CLI. A custom way.

Create Angular applications with a Webpack based tooling.

Official doc: https://angular.io/docs/ts/latest/guide/webpack.html

Initializing the project

  • We run
    npm init

to create a package.json.

Setting up the basic project files

  • Lets make the folder structure and blank files:
    • src folder
    • src/app folder
    • src/app/main.ts: Compiles the application with the JIT compiler and bootstraps the application’s main module (AppModule) to run in the browser. The JIT compiler is a reasonable choice during the development of most projects and it’s the only viable choice for a sample running in a live-coding environment like Plunker.
    • src/app/home.component.ts
    • src/app/app.module.ts:  Defines AppModule, the root module that tells Angular how to assemble the application. Right now it declares only the AppComponent. Soon there will be more components to declare.
    • src/app/app-routing.module.ts
    • src/app/app.component.ts:  It is the root component of what will become a tree of nested components as the application evolves.
    • src/app/app.component.html
    • src/app/app.component.css
    • src/index.html
    • src/pollyfills.js
    • src/app/users/users.module.ts
    • src/app/users/users,component.ts
    • src/app/users/users.component.html
    • src/app/users/users.component.css
    • src/app/users/users-routing.module.ts

Installing the core dependencies

Lets install all the packages needed with npm install:

npm install --save @angular/core @angular/compiler @angular/common @angular/compiler-cli @angular/forms @angular/http @angular/platform-browser @angular/platform-browser-dynamic @angular/platform-server @angular/router @angular/upgrade @angular/animations
typescript rxjs zone.js core-js

Filling the Project Files with some life

  • We create some basic content for all the components.

index.html & pollyfills

./polyfills.js: 

You’ll need polyfills to run an Angular application in most browsers as explained in the Browser Support guide. Polyfills should be bundled separately from the application and vendor bundles. Add a polyfills.ts like this one to the src/folder.

import 'core-js/es6';
import 'core-js/es7/reflect';
require('zone.js/dist/zone');

./index.html

<!doctype html>
<html class="no-js" lang="">
    <head>
        <base href="/">
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title>Angular seed</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <link rel="apple-touch-icon" href="apple-touch-icon.png">
        <!-- Place favicon.ico in the root directory -->

        <link rel="stylesheet" href="css/normalize.css">
        <link rel="stylesheet" href="css/main.css">
    </head>
    <body>
      <app-root>Loading...</app-root>
    </body>
</html>

Installing development dependencies

sudo npm install --save-dev webpack webpack-dev-server angular-router-loader angular2-template-loader awesome-typescript-loader html-loader raw-loader del-cli

Setting up a development workflow: Typescript configuration

TypeScript is a primary language for Angular application development. It is a superset of JavaScript with design-time support for type safety and tooling.Browsers can’t execute TypeScript directly. Typescript must be “transpiled” into JavaScript using the tsc compiler, which requires some configuration.

Typescript wiki: http://www.typescriptlang.org/docs/handbook/tsconfig-json.html

Typescript compiler options: http://www.typescriptlang.org/docs/handbook/compiler-options.html

./tsconfig.json:TypeScript compiler configuration

{
  "compilerOptions": {
    "moduleResolution": "node",  
    "emitDecoratorMetadata": true,  
    "experimentalDecorators": true,
    "target":"es5",
    "lib": [
      "es5",
      "dom"
    ]
  }
}
  • emitDecoratorMetadata: true and experimentalDecorators:true because we are using decorators.
  • target:es5 to run in all browsers

Setting up a development workflow:Webpack

Webpack is a powerful module bundler. A bundle is a JavaScript file that incorporates assets that belong together and should be served to the client in a response to a single file request. A bundle can include JavaScript, CSS styles, HTML, and almost any other kind of file.

Webpack roams over your application source code, looking for import statements, building a dependency graph, and emitting one or more bundles. With plugins and rules, Webpack can preprocess and minify different non-JavaScript files such as TypeScript, SASS, and LESS files.

You determine what Webpack does and how it does it with a JavaScript configuration file, webpack.config.js.

  • And we create three different webpack config files: webpack.config.common.js , webpack.config.dev.js and webpack.config.prod.js
  • For merging webpack config files we need to install a new npm package:
sudo npm install --save-dev webpack-merge
  • we also need to install

    HtmlWebpackPlugin

    Webpack generates a number of js and CSS files. You could insert them into the index.html manually. That would be tedious and error-prone. Webpack can inject those scripts and links for you with the HtmlWebpackPlugin.

sudo npm install --save-dev html-webpack-plugin

./webpack.config.common.js:

The webpack.common.js configuration file does most of the heavy lifting. Create separate, environment-specific configuration files that build on webpack.common by merging into it the peculiarities particular to the target environments.

These files tend to be short and simple.

module.exports = {
  entry: './src/app/main.ts',

  resolve: {
    extensions: ['.js', '.ts']
  },

  module: {
    rules: [
      {
        test: /\.html$/,
        loaders: ['html-loader']
      },
      {
        test: /\.css$/,
        loaders: ['raw-loader']
      }
    ],
    exprContextCritical: false
  },
 plugins: [
   new HtmlWebpackPlugin({
   template: 'src/index.html'
 })
 ]
};

./webpack.config.dev.js

var webpackMerge = require('webpack-merge');
const path = require('path');

var commonConfig = require('./webpack.config.common');

module.exports = webpackMerge(commonConfig, {
  devtool: 'cheap-module-eval-source-map',
  output: {
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/',
    filename: 'bundle.js',
    chunkFilename: '[id].chunk.js'
  },

  module: {
    rules: [
      {
        test: /.ts$/,
        use: [
          {loader: 'awesome-typescript-loader', options: {
            transpileOnly: true
          }},
          {loader: 'angular2-template-loader'},
          {loader: 'angular-router-loader'}
        ]

      }
    ]
  },

  devServer: {
    historyApiFallback: true,
    stats: 'minimal'
  }
});

 

Webpack config: Entries and outputs

You supply Webpack with one or more entry files and let it find and incorporate the dependencies that radiate from those entries. The one entry point file in this example is the application’s root file, src/main.ts:

entry: {
  'app': './src/main.ts'
},

Webpack inspects that file and traverses its import dependencies recursively.

Webpack config: Loaders

Webpack can bundle any kind of file: JavaScript, TypeScript, CSS, SASS, LESS, images, HTML, fonts, whatever. Webpack itself only understands JavaScript files. Teach it to transform non-JavaScript file into their JavaScript equivalents with loaders. Configure loaders for TypeScript and CSS as follows.

rules: [
  {
    test: /\.ts$/,
    loader: 'awesome-typescript-loader'
  },
  {
    test: /\.css$/,
    loaders: 'style-loader!css-loader'
  }
]

Webpack config: Plugins

Webpack has a build pipeline with well-defined phases. Tap into that pipeline with plugins such as the uglify minification plugin:

plugins: [
  new webpack.optimize.UglifyJsPlugin()
]

Finishing and using the development workflow

  • Lets modify our package.json to run the local development server:

package.json

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack-dev-server --inline --progress --port 8080 --config webpack.config.dev.js"
  },
  • Now we can run
    npm run build

and hit localhost:8080 in the browser

Setting up a production workflow

  • For make a build for production we want to add AoT (Ahead of time compilation) to optimize loading of the app.
  • Let first create versions of the config files implementing AoT.
  • ./src/app/main.aot.ts 
import '../polyfills';

import {platformBrowser} from '@angular/platform-browser';
import {enableProdMode} from '@angular/core';

import {AppModuleNgFactory} from './app.module.ngfactory';

enableProdMode();
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);

NOTE 1: app.module.ngfactory will be generated in the time of building for production with aot.

NOTE 2: For bootstrapping angular this time we are using platform-browser instead of platform-browser-dynamic. The difference between platform-browser-dynamic and platform-browser is the way your angular app will be compiled.
Using the dynamic platform makes angular sending the Just-in-Time compiler to the front-end as well as your application. Which means your application is being compiled on client-side.
On the other hand, using platform-browser leads to an Ahead-of-Time pre-compiled version of your applicatiion being sent to the browser. Which usually means a significantally smaller package being sent to the browser.
The angular2-documentation for bootstrapping at https://angular.io/docs/ts/latest/guide/ngmodule.html#!#bootstrap explains it in more detail.

./tsconfig.aot.json

{
  "compilerOptions": {
    "module": "es2015",
    "outDir":"./dist",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target":"es5",
    "lib": [
      "es5",
      "dom"
    ]
  },
  "angularCompilerOptions": {
    "skipMetadataEmit":true
  }
}

./webpack.config.prod.js

var webpackMerge = require('webpack-merge');
const path = require('path');
var webpack = require('webpack');

var commonConfig = require('./webpack.config.common');

module.exports = webpackMerge(commonConfig, {
  entry: './src/app/main.aot.ts',
  devtool: 'cheap-module-eval-source-map',
  output: {
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/',
    filename: '[hash].js',
    chunkFilename: '[id].[hash].chunk.js'
  },

  module: {
    rules: [
      {
        test: /.ts$/,
        use: [
          {loader: 'awesome-typescript-loader'},
          {loader: 'angular2-template-loader'},
          {loader: 'angular-router-loader?aot=true'}
        ]

      }
    ]
  },

  plugins: [
    new webpack.optimize.UglifyJsPlugin()
  ]
});

package.json

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack-dev-server --inline --progress --port 8080 --config webpack.config.dev.js",
    "build:prod":"del-cli dist && ngc -p tsconfig.aot.json && ngc -p tsconfig.aot.json && webpack --config webpack.config.prod.js --progress --profile --bail && del-cli 'src/app/**/*.js' 'src/app/**/*.ngfactory.ts' 'src/app/**/*.js.map' 'src/app/**/*.shim.ts' 'src/app/**/*.ngsummary.json'"
  },
  • In the build:prod script , we first clean the dist folder , then we compiler typescripts files with angular offline compiler (ngc), then we make the bundle with webpack and finally we clean files not needed.

Adding types and fixing bugs

  • For resolving a bug in the compilation we need to install two more packages: @types/core-js and @types/node. NOTE : be sure to fit to these versions:
    "@types/core-js": "^0.9.36",
    "@types/node": "^6.0.45",

and modify tsconfig.aot.json to :

    "target":"es5",
    "typeRoots": [
      "node_modules/@types"
    ],
  • Now we are ready to run
    npm run build:prod

this will generate a dist folder with a build release for production.

Finishing touches

  • Let finally create a lite server for testing our build release for production.
  • Lets install npm package lite-server
npm install --save-dev lite-server
  • And create a config file for that server:

bs-config.js

module.exports = {
  server: {
    baseDir: './dist',
    middleWare: {
      1: require('connect-history-api-fallback')({ index:'/index.html', verbose:true })
    }
  }
};

and a new script “serve” in the package.json:

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack-dev-server --inline --progress --port 8080 --config webpack.config.dev.js",
    "build:prod": "del-cli dist && ngc -p tsconfig.aot.json && ngc -p tsconfig.aot.json && webpack --config webpack.config.prod.js --progress --profile --bail && del-cli 'src/app/**/*.js' 'src/app/**/*.ngfactory.ts' 'src/app/**/*.js.map' 'src/app/**/*.shim.ts' 'src/app/**/*.ngsummary.json' 'src/app/**/*.ngstyle.ts' 'dist/app'",
    "serve": "lite-server"
  },

and we are ready to run

 npm run serve

and see our prod release live!

Angular 2 – What changed with Angular 4?

Backward compatibility

Angular 4 is NOT a complete re-write of Angular 2, indeed, it shouldn’t introduce any breaking changes. Some features will be deprecated (i.e. they still work but will get replaced with the next major Angular version), some new features were added.

Regarding your old code all will continue to work.

 Changes

  • ng-if with else

// Syntax for ngIf/Else
<div *ngIf=”condition; else elseBlock”>Truthy condition</div>
<template #elseBlock>Falsy condition</template>
  • A new Renderer: Renderer2

Just make sure to check import and dependency injections with Rendere2 instead of Renderer

  • Email validator with email directive

Angular 4 has a built-in “email” validation tag that can be added within the input. E.g.:

<input type="email" id="contactemail" email>

Under the hood improvements:

  • Typecript 2.2

Check https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html

  • Flat ES Modules (Flat ESM / FESM)

    We now ship flattened versions of our modules (“rolled up” version of our code in the EcmaScript Module format, see example file). This format should help tree-shaking, help reduce the size of your generated bundles, and speed up build, transpilation, and loading in the browser in certain scenarios.
     
    Read more about the importance of Flat ES Modules in “The cost of small modules”.

Deprecations

  • Renderer v1
  • Angular animation package from core –> now will import from angular/animations

 

Angular2 – Unit testing – introduction

Introduction

This Section only provides an Introduction to Unit Testing an Angular 2 App.

It’s NOT a detailed Guide about that neither is it a detailed Introduction to Unit Testing in general. Check out the “Further Resources” (last Lecture in this Module) to dive deeper into these topics.

Why units tests?

Captura de pantalla 2017-06-02 a las 11.48.17

Captura de pantalla 2017-06-02 a las 11.48.34

Analyzing the testing setup (as created by the CLI)

  • In order to simulate we need to run the app in the testing environment as it would be in the browser.
  • Our test runner will mount the testing environment and will read spec files to execute the tests.
  • Lets see one example
  • app/app.component.spec.ts
import { TestBed, async } from '@angular/core/testing';

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

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
    }).compileComponents();
  }));

  it('should create the app', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  }));

  it(`should have as title 'app'`, async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app.title).toEqual('app');
  }));

  it('should render title in a h1 tag', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!!');
  }));
});
  • In the beforeEach we configure the module for our testing with the TestBed utility.
  • toBeTruthy check there is an existing app
  • In the third test we check one value in the template. For that we need to

Running Tests (with the CLI)

ng test

Adding a Component and some fitting tests

  • Lets create a new user component: ng g c user
  • Lets create a basic test for this component: check if instance is existing

app/user/user.component.ts

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

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

  user: {name: string};
  isLoggedIn = false;

  constructor() { }

  ngOnInit() {
  }

}

app/user/user.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { UserComponent } from './user.component';

describe('UserComponent', () => {
  let component: UserComponent;
  let fixture: ComponentFixture<UserComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ UserComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(UserComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should be created', () => {
    expect(component).toBeTruthy();
  });
});

app/user/user.component.html

<div *ngIf="isLoggedIn">
  <h1>User logged in</h1>
  <p>User is {{ user.name }}</p>
</div>
<div *ngIf="!isLoggedIn">
  <h1>User not logged in</h1>
  <p>Please log in first</p>
</div>

 

Testing Dependencies: Components and Services

  • Lets add a service component

user.service.ts

export class UserService {
  user = {
    name: 'Max'
  };
}

user.component.spec.ts

  it('should use the user name from the service', ()=> {
    let userService = fixture.debugElement.injector.get(UserService);
    fixture.detectChanges();
    expect(userService.user.name).toEqual(component.user.name);
  });
  it('should display the user name if user is logged in', ()=> {
    component.isLoggedIn = true;
    fixture.detectChanges();
    let compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('p').textContent).toContain(component.user.name);
  });
  it('should not display the user name if user is not logged in', ()=> {
    fixture.detectChanges();
    let compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('p').textContent).not.toContain(component.user.name);
  });

 

Simulating Asyncs Tasks

  • We create a data service to simulate a remote service
  • In the test we wrap tests with async method and we use fixture.whenStable to check the assertions.

/shared/data.service.ts

export class DataService {
  getDetails() {
    const resultPromise = new Promise((resolve,reject)=>{
      setTimeout(()=> {
        resolve('Data');
      },1500);
    });
    return resultPromise;
  }
}

/user/user.component.ts

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

import {UserService} from './user.service';
import {DataService} from '../shared/data.service';

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css'],
  providers: [UserService, DataService]
})
export class UserComponent implements OnInit {

  user: {name: string};
  isLoggedIn = false;
  data:string;

  constructor(private userService: UserService, private dataService: DataService) { }

  ngOnInit() {
    this.user = this.userService.user;
    this.dataService.getDetails().then((data:string)=> this.data = data);
  }

}

/user/user.component.spec.ts

  it('shouldnt fetch data successfully if not called asynchronously', ()=> {
    let dataService = fixture.debugElement.injector.get(DataService);
    let spy = spyOn(dataService, 'getDetails').and.returnValue(Promise.resolve('Data'));
    fixture.detectChanges();
    expect(component.data).toBe(undefined);
  });

  it('should fetch data successfully if called asynchronously', async(()=> {
    let dataService = fixture.debugElement.injector.get(DataService);
    let spy = spyOn(dataService, 'getDetails').and.returnValue(Promise.resolve('Data'));
    fixture.detectChanges();
    fixture.whenStable().then(()=> {
      expect(component.data).toBe('Data');
    });
  }));

 

Using “fakeAsync” and “tick”

  • It is another way for testing and async feature:
  it('should fetch data successfully if called asynchronously', async(()=> {
    let dataService = fixture.debugElement.injector.get(DataService);
    let spy = spyOn(dataService, 'getDetails').and.returnValue(Promise.resolve('Data'));
    fixture.detectChanges();
    fixture.whenStable().then(()=> {
      expect(component.data).toBe('Data');
    });
  }));

 

Isolated vs Non-isolated tests

  • There can be some services or pipes with functions not related to Angular. We just make simple tests for those
  • For example a reverse pipe and its test

/shared/reverse.pipe.ts

import { Pipe } from '@angular/core';
@Pipe({
  name: 'reverse'
})
export class ReversePipe {
  transform(value: string) {
    return value.split("").reverse().join("");
  }
}

/shared/reverse.pipe.spec.ts

import { ReversePipe } from './reverse.pipe';

describe('ReversePipe', () => {

  it('should be created', () => {
    let reversePipe = new ReversePipe();
    expect(reversePipe.transform('hello')).toEqual('olleh');
  });
});

 

Further resources & Where to go next

This Module only provides a brief and basic Introduction to Angular 2 Unit Tests and the Angular 2 Testing Suite. This Course isn’t focused on Testing.

If you want to dive deeper, the official Docs actually are a great place to start. There you’ll also find a Non-CLI Setup!

Official Docshttps://angular.io/docs/ts/latest/guide/testing.html

I can also recommend the following Article: https://semaphoreci.com/community/tutorials/testing-components-in-angular-2-with-jasmine

For more Information on how to run Tests with the CLI have a look at their official Docs:

=> Unit Tests: https://github.com/angular/angular-cli#running-unit-tests

=> E2E Tests: https://github.com/angular/angular-cli#running-end-to-end-tests

 

NPM as a build tool

[Fuente: https://www.keithcirkel.co.uk/how-to-use-npm-as-a-build-tool/]

Detailed info in https://docs.npmjs.com/misc/scripts

npm a great package manager, but npm has a great subset of functionality dedicated to running tasks to facilitate in a packages lifecycle – in other words, it is a great tool for build scripts.

npm Scripts

Firstly, we need to figure out how npm can manage our build scripts. As part of npm’s core, it has the

npm run-script

command (npm run for short). This command dives into your package.json and pulls out the scripts Object. The first argument passed to npm run refers to a property in the scripts objectit will execute the property’s value as a command in the operating systems default shell (usually Bash, except on Windows – but we’ll get to that later). So let’s say you have a package.json config that looks like this:

{
  "name": "myproject",
  "devDependencies": {
    "jshint": "latest",
    "browserify": "latest",
    "mocha": "latest"
  },
  "scripts": {
    "lint": "jshint **.js",
    "test": "mocha test/"
  }
}

If you run npm run lint – npm will spawn a shell and run jshint **.js. If you run npm run test, npm will spawn a shell and run mocha test/. The shell environment has your node_modules/.bin folder added to the PATH which means any of the dependencies you have that install binaries will be runnable directly – in other words, no need to put "./node_modules/.bin/jshint **.js" or "$(npm bin)/jshint **.js". If you run npm run without any arguments it gives you a list of the available commands to run, like so:

Available scripts in the user-service package:
  lint
     jshint **.js
  test
    mocha test/

Shortcut scripts

npm also provides a few convinient shortcuts. The

npm test, npm start, npm stop

 commands are all shortcuts for their run equivalents, e.g. npm test is just a shortcut for npm run test. These shortcuts are useful for 2 reasons:

  1. These are common tasks that most projects will use, and so it’s nice to not have to type as much each time.
  2. Much more importanlty – it provides a standard interface within npm for testing, starting and stopping a package. Many CI tools, such as Travis, take advantage of this behaviour by making the default command for a Node.js project npm test. It also makes introducing new developers to your project a breeze, if they know they can simply run scripts like npm test without ever having to read any docs.

Pre and Post Hooks

Another cool feature about npm is that any script that can be executed also has a set of pre- and post- hooks, which are simply definable in the scripts object.

For example, if you execute npm run lint, despite npm having no preconceived idea of what the lint task is, it will immediately run npm run prelint, followed by npm run lint, followed by npm run postlint. The same is true for any command, including npm test (npm run pretest, npm run test, npm run posttest). The pre and post scripts are also exit-code-sensitive, meaning if your pretest script exits with a non-zero exit code, then NPM will immediately stop, and not run the test and posttest scripts. You can’t pre- a pre- script though, so prepretest gets ignored.

npm also runs the pre- and post- hooks for a few internal commands:

install , uninstall, publish, update

. You can’t override the behaviours for the internal commands – but you can affect their behaviour with pre- and post- scripts. This means you can do cool stuff like:

  "scripts": {
    "lint": "jshint **.js",
    "build": "browserify index.js > myproject.min.js",
    "test": "mocha test/",

    "prepublish": "npm run build # also runs npm run prebuild",    
    "prebuild": "npm run test # also runs npm run pretest",
    "pretest": "npm run lint"
  }

Passing Arguments

Another cool feature with npm (since npm 2.0.0, at least) is passing argument sets through to the underlying tools. This can be a little complex, but here’s an example:

  "scripts": {
    "test": "mocha test/",
    "test:xunit": "npm run test -- --reporter xunit"
  }

With this config, we can simply run

npm run test

– which runs mocha test/, but we can extend it with custom parameters with a -- prefix. For example npm

run test -- anothertest.js

 will run mocha test/ anothertest.js, or more usefully

npm run test -- --grep parser

will expand to mocha test/ --grep parser (which runs only the tests with “parser” in the title). In the package.json we have test:xunit which effectively runs mocha test --reporter xunit. This setup can be incredibly useful for composing commands together for some advanced configurations.

NPM Config Variables

One last thing that is worth mentioning – npm has a config directive for your package.json. This lets you set arbitrary values which can be picked up as environment variables in your scripts. Here’s an example:

  "name": "fooproject",
  "config": {
    "reporter": "xunit"
  },
  "scripts": {
    "test": "mocha test/ --reporter $npm_package_config_reporter",
    "test:dev": "npm run test --fooproject:reporter=spec"
  }

Here, the config object has reporter property – set to 'xunit'. All config options are exposed as environment variables prefixed with npm_package_config_ (which, admittedly, does make the variable names a mouthful). In the above example, the npm run test command uses the $npm_package_config_reporter variable which gets expanded to mocha test/ --reporter xunit. This can get replaced in two convinient ways:

  1. Just like the test:dev task, you can change the reporter config variable to spec– by using
    --fooproject:reporter

    . You’d replace fooproject with your project’s name, and reporter with the config variable to override.

  2. They can also be overriden as part of a user’s config. By running
    npm config set fooproject:reporter spec

    – an entry in my ~/.npmrc appears (fooproject:reporter=spec) which is read at runtime, and overrides the npm_package_config_reporter variable, meaning that on my local machine, forever more, npm run test gets expanded to mocha test/ --reporter spec. I can remove my personalised setting for this using npm config delete fooproject:mocha_reporter. A good set up for this is to have some sensible defaults in your package.json – but your own customisations can be tucked away inside your own ~/.npmrc.

Now, if I’m totally honest I’m not in love with the way this works. While the setup seems trivial (having a "config" object in your JSON), trying to use them is seems too verbose and complicated. I would also like a simpler way to override package configs, without specifying the package’s name – it’d be awesome if standard conventions could come into place where I could set my favourite mocha reporter for all packages in my ./.npmrc.

The other downside to these configs is that they’re not very Windows friendly – Windows uses % for variable substitution, while bash uses $. They work fine if you use them within a Node.js script, but if anyone knows of a way to get them working in Windows via the shell commands, let me know!

The Windows Problem

Let’s get something out of the way before we progress. Because npm is reliant on the operating systems shell to run scripts commands, they can quickly become unportable. While Linux, Solaris, BSD and Mac OSX come preinstalled with Bash as the default shell, Windows does not. On Windows, npm will resort to using Windows command prompt for these things.

Frankly, this is less of a problem than it seems. A good chunk of syntax that works in Bash will also work in Windows command prompt the same way:

  • && for chaining tasks
  • & for running tasks simaltaneously
  • < for inputting the contents (stdin) of a file to a command
  • > for redirecting output (stdout) of a command and dumping it to a file
  • | for redirecting output (stdout) of a command and sending it to another command

The biggest problems between the two is the availability and naming of commands (e.g. cp is COPY in Windows) and variables (Windows uses % for variables, Bash $). I feel like both of these are completely surmountable problems:

  1. Rather than relying on built in commands, you could simply use alternatives – for example instead of using rm, use the npm rimraf package.
  2. Rather than trying to use syntax that is not cross compatible, stick to just the above ones. You’d be surprised just how much you can get done with just &&, >, |, and <. Variables are for suckers anyway.

Replacing build tools

Ok, lets get to the brass tacks of this post. If we want to replace build tools like Grunt or Gulp, we need like-for-like replacements for plugins and features of these tools. I’ve taken the most popular tasks & paradigms from various projects, and questions from commenters of my last post and demonstrated how to do them in npm:

Using multiple files

I had a few people responding to my last post, saying the benefit of task runners is their ability to handle multiple files in tasks using file “globs” which look like *.js, *.min.css or assets/*/*. This feature was actually inspired from Bash, which in turn was inspired from the glob command from Unix in 1969. The shell will automatically look at a command line arguments such as *.js and expand the stars out as wildcards. Using two stars allows it to search recursively. If you’re on a Mac or Linux machine, try opening up your shell and playing with it (try something like ls *.js).

Now, the problem herein lies that, of course, the Windows command line does not have this functionality. Luckily, when given a command line argument like *.js – Windows passes it verbatum to the application, meaning that tool vendors can install compatibility libraries to give Windows glob like functionality. Many, many tools on npm do; the two most popular glob libraries, minimatch and glob, share 1500 dependents, including JSHint, JSCS, Mocha, Jade, Stylus, Node-Sass… the list goes on.

This means you can just use file globs within npm scripts, like so:

"devDependencies": {
  "jshint": "latest"
},
"scripts": {
  "lint": "jshint *.js"
}

Running multiple tasks

Grunt, Gulp etc all have the capability of tying multiple tasks up together to make one single task – typically useful for building or testing. With npm you have two options here – depending on which one is semantically the right fit. You can either use the pre- or post- hooks – which are a good fit if the task is a prerequisite thing (i.e concating js before minfiying it), or you can use the bash && operator – like so:

"devDependencies": {
  "jshint": "latest",
  "stylus": "latest",
  "browserify": "latest"
},
"scripts": {
  "lint": "jshint **",
  "build:css": "stylus assets/styles/main.styl > dist/main.css",
  "build:js": "browserify assets/scripts/main.js > dist/main.js",
  "build": "npm run build:css && npm run build:js",
  "prebuild:js": "npm run lint"
}

In the above example build will execute both build:css and build:js – but not before running the lint task. Using this pattern you can also run the build:css or build:js tasks separately, and build:js will also run lint beforehand. Tasks can be composed and chained like this as much as you like, and it is all Windows compatible.

Streaming to multiple tasks

One of Gulp’s biggest features is that it streams the output seamlessly from one task to the next (as opposed to Grunt which constantly dips in and out of the filesystem). Bash and the Windows command line have the pipe operator (|), which can stream one command’s output (stdout) and send it to another command’s input (stdin). Let’s say you want to run all of your CSS first through Autoprefixer, then CSSMin, then output to a file (using the > operator, which outputs stdout to a given file):

"devDependencies": {
  "autoprefixer": "latest",
  "cssmin": "latest"
},
"scripts": {
  "build:css": "autoprefixer -b 'last 2 versions' < assets/styles/main.css | cssmin > dist/main.css"
}

As you can see autoprefixer adds the CSS vendor prefixes to our CSS, which is then piped to cssmin which minifies the output – then the whole thing gets dumped into dist/main.css. Most good tools will support stdin and stdout and the above code is fully compatible with Windows, Mac and Linux.

Version Bumping

Version bumping is a popular Grunt or Gulp task. Effectively it increments the version number up by one inside the package.json, makes a git commit, and tags said commit.

This actually comes baked into npm (it is a package manager after all). Simply run

npm version patch

to increment the patch number (e.g. 1.1.1 -> 1.1.2),

npm version minor

to increment the minor version number (e.g. 1.1.1 -> 1.2.0) or

npm version major

(e.g. 1.1.1 -> 2.0.0). It’ll commit and tag up your package for you, all that is left is to git push and npm publish.

This can be fully customised too. For example, if you don’t want it running git tag, simply run it with the --git-tag-version=false flag (or set it to permanently not with npm config set git-tag-version false). Want to configure the commit message? Simply run it with the -m flag, e.g. npm version patch -m "Bumped to %s" (set it permanently with npm config set message "Bumped to %s"). You can even get it to sign the tags for you, by running with the --sign-git-tag=true flag (or, once again, set it permanently with npm config set sign-git-tag true).

Clean

Many build runners come with a clean task. This task usually just removes a bunch of files so you can start with a fresh working copy to start building into. Well, turns out that Bash has a pretty good clean command all by itself: rm. Passing the -r (recursive) flag lets rm remove directories too! It couldn’t be simpler:

"scripts": {
  "clean": "rm -r dist/*"
}

If you really need to have Windows support, it does not support rm – luckily there is rimraf which is a cross-compatible tool to do the same thing:

"devDependencies": {
  "rimraf": "latest"
},
"scripts": {
  "clean": "rimraf dist"
}

Compiling files to unique names

Effectively, trying to replace the functionality of gulp-hash and grunt-hashtake an input of JS and name it with the hash of its contents. This one turned out to be really complex to do using existing command line tools, so I had a look on npm to see if anything fit the bill, and it didn’t – so I wrote one (I can hear the Grunt/Gulp proponents telling me I cheated already). I have two points about this – firstly, it’s pretty disappointing to see lots of siloed efforts from various plugin authors – and no generic solutions that work with any build tool. Secondly – if you can’t find something that fits, write your own! My hashmark library clocks in around the same lines of code as the grunt/gulp versions, and has a similar or better featureset, depending on the plugin – mine even supports streaming! Going back to the autoprefixer example, we can output a file with a specific hash using pipes:

"devDependencies": {
  "autoprefixer": "latest",
  "cssmin": "latest"
},
"scripts": {
  "build:css": "autoprefixer -b '> 5%' < assets/styles/main.css | cssmin | hashmark -l 8 'dist/main.#.css'"
}

Now the ouput of build:css will ouput a file in dist named with a hash, such as dist/main.3ecfca12.css.

Watch

This is definitely the most popular reason why people using Grunt/Gulp, and by far the most requested example from comments around my previous post. A lot of these build tools come with commands for watching a filesystem, detecting changes to files (e.g. from saving), and then reloading the server/recompiling assets/rerunning tests. Very useful for rapid development. It seems like most developers replying to my last post simply assumed that this wasn’t an option outside of Grunt/Gulp (or perhaps thought it was something too difficult to do without them).

Well, most tools facilitate this option themselves – and usually are much more in tune with the intricacies of the files that should be listened for. For example Mocha has the -w option, as does Stylus, Node-Sass, Jade, Karma, and others. You could use these options like so;

"devDependencies": {
  "mocha": "latest",
  "stylus": "latest"
},
"scripts": {
  "test": "mocha test/",
  "test:watch": "npm run test -- -w",

  "css": "stylus assets/styles/main.styl > dist/main.css",
  "css:watch": "npm run css -- -w"
}

Of course, not all tools support this, and even when they do – you might want to compose multiple compile targets into one task which watches for changes and runs the whole set. There are tools that watch files and execute commands when files change, for example watch, onchange, dirwatch, or even nodemon:

"devDependencies": {
  "stylus": "latest",
  "jade": "latest",
  "browserify": "latest",
  "watch": "latest",
},
"scripts": {
  "build:js": "browserify assets/scripts/main.js > dist/main.js",
  "build:css": "stylus assets/styles/main.styl > dist/main.css",
  "build:html": "jade assets/html/index.jade > dist/index.html",
  "build": "npm run build:js && npm run build:css && npm run build:html",
  "build:watch": "watch 'npm run build' .",
}

There you go – pretty painless. This 13 lines of JSON will watch our whole project directory, and build HTML, CSS and JS assets every time any file changes. Just run npm run build:watch and start developing! You could even optimise this further, with a little tool I wrote (once again, while writing this post): Parallelshell, which will keep multiple processes running at one time – a little like this:

"devDependencies": {
  "stylus": "latest",
  "jade": "latest",
  "browserify": "latest",
  "watch": "latest",
  "parallelshell": "latest"
},
"scripts": {
  "build:js": "browserify assets/scripts/main.js > dist/main.js",
  "watch:js": "watch 'npm run build:js' assets/scripts/",
  "build:css": "stylus assets/styles/main.styl > dist/main.css",
  "watch:css": "watch 'npm run build:css' assets/styles/",
  "build:html": "jade index.jade > dist/index.html",
  "watch:html": "watch 'npm run build:html' assets/html",
  "build": "npm run build:js && npm run build:css && npm run build:html",
  "build:watch": "parallelshell 'npm run watch:js' 'npm run watch:css' 'npm run watch:html'",
}

Now running npm run build:watch will run the individual watchers all through Parallelshell and if, for example, you only change the CSS, then only the CSS will recompile. If you change the JS then only the JS will recompile and so on. Parallelshell combines the outputs (stdout and stderr) of each of the tasks, and will listen to the exit code to ensure logs and failed builds propagate out (unlike the Bash/Windows & operator).

LiveReload

LiveReload was another popular one. If you don’t know what LiveReload is – its a combination of command line tool and browser extension (or custom server) – as files change, LiveReload triggers the page you’re looking at to reload meaning you never have to press refresh. The npm package live-reload is a pretty suitable command line client for this – it runs a server which only serves a JS file, which if you include on your page will notify the page of changes. Simple, yet effective. Here’s an example of how to get it working:

"devDependencies": {
  "live-reload": "latest",
},
"scripts": {
  "livereload": "live-reload --port 9091 dist/",
}
<!-- In your HTML file -->
<script src="//localhost:9091"></script>

Now running npm run livereload – when you visit the HTML page it’ll start listening to the livereload server. Any changes to files in the dist/ directory will notifiy clients, and the page will be reloaded.

Running tasks that don’t come with binaries

It was pointed out to me that there are libs that don’t come with binaries – such as favicon – and so Grunt/Gulp plugins can be useful because they wrap the tools so they can be used within the task runners. If you find a package that you want to use, but it doesn’t have a binary then simply write some JavaScript! You would have to if using Grunt or Gulp, so don’t be afraid to just chuck a bit of JavaScript somewhere that wires it all up (or even better, submit a PR to the maintainers convincing them to support a command line interface!):

// scripts/favicon.js
var favicons = require('favicons');
var path = require('path');
favicons({
    source: path.resolve('../assets/images/logo.png'),
    dest: path.resolve('../dist/'),
});
"devDependencies": {
  "favicons": "latest",
},
"scripts": {
  "build:favicon": "node scripts/favicon.js",
}

A fairly complex config

In my previous post many were telling me I was missing the point about task runners – they’re for wiring up complex sets of tasks, not just running odd tasks. So I thought I’d wrap up this piece with a complex set of tasks typical of a multi-hundred-line Gruntfile. For this example I want to do the following:

  • Take my JS and lint, test & compile it into 1 versioned file (with a separate sourcemap) and upload it to S3
  • Compile Stylus into CSS, down to a single, versioned file (with separate sourcemap), upload it to S3
  • Add watchers for testing and compilation
  • Add a static file server to see my single page app in a web browser
  • Add livereload for CSS and JS
  • Have a task that combines all these files so I can type one command and spin up an environment
  • For bonus points, open a browser window automagically pointing to my website

I’ve chucked up a simple repository on GitHub called npm-scripts-example. It contains the layout for a basic website, and a package.json to fit the above tasks. The lines you’re probably interested in are:

  "scripts": {
    "clean": "rimraf dist/*",

    "prebuild": "npm run clean -s",
    "build": "npm run build:scripts -s && npm run build:styles -s && npm run build:markup -s",
    "build:scripts": "browserify -d assets/scripts/main.js -p [minifyify --compressPath . --map main.js.map --output dist/main.js.map] | hashmark -n dist/main.js -s -l 8 -m assets.json 'dist/{name}{hash}{ext}'",
    "build:styles": "stylus assets/styles/main.styl -m -o dist/ && hashmark -s -l 8 -m assets.json dist/main.css 'dist/{name}{hash}{ext}'",
    "build:markup": "jade assets/markup/index.jade --obj assets.json -o dist",

    "test": "karma start --singleRun",

    "watch": "parallelshell 'npm run watch:test -s' 'npm run watch:build -s'",
    "watch:test": "karma start",
    "watch:build": "nodemon -q -w assets/ --ext '.' --exec 'npm run build'",

    "open:prod": "opener http://example.com",
    "open:stage": "opener http://staging.example.internal",
    "open:dev": "opener http://localhost:9090",

    "deploy:prod": "s3-cli sync ./dist/ s3://example-com/prod-site/",
    "deploy:stage": "s3-cli sync ./dist/ s3://example-com/stage-site/",

    "serve": "http-server -p 9090 dist/",
    "live-reload": "live-reload --port 9091 dist/",

    "dev": "npm run open:dev -s & parallelshell 'npm run live-reload -s' 'npm run serve -s' 'npm run watch -s'"
  }

(If you’re wondering what the -s flag is, it just silences output from npm on those tasks, cleaning up the log output, try disabling them to see the difference)

To do the equivalent in Grunt, it’d take a Gruntfile of a few hundred lines, plus (my finger in the air estimate) around 10 extra dependencies. It’s certainly subjective as to which version would be more readable – and while npm is certainly not the holy grail of readability, I personally think the npm scripts directive is easier to reason about (i.e. I can see all tasks and what they do, at a glance).

Conclusion

Hopefully this article shows you how capable npm can be as a build tool. Hopefully it has demonstrated to you that tools like Gulp and Grunt should not always be the first thing to jump to in a project, and that tools you probably already have on your system are worth investigating.

As always, feel free to discuss this with me on The Twitter, I’m @keithamus, you can “Follow” me there too, apparently.

Angular2 – Angular animations

Making Animations work with Angular 4

With the release of Angular 4, the general syntax of Angular Animations didn’t change.

However, the animation functions were moved into their own package and you now also need to add a special module to your imports[]  array in the AppModule.

Specifically, the following adjustments are required:

  • You probably need to install the new animations package (running the command never hurts):
    npm install --save @angular/animations
  • Add the BrowserAnimationsModule  to your imports[]  array in AppModule
  • This Module needs to be imported from @angular/platform-browser/animations'  =>
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations'

    (in the AppModule!)

  • You then import trigger , state , style  etc from @angular/animations  instead of @angular/core

That’s all!

Introduction

  • Angular comes with a built-in animation module.
  • It is hard to handle transition animation with only CSS in cases of render items in a list or conditional display of DOM elements . Thats why Angular provides this module.

Setting up the starting project

  • We will start putting animation on a box below the buttons
  • We will define states from where the transition will start and will end to.

Animations triggers and state and switching between states

  • We will change the box from red to blue and move it to the right on the click of Animate button.

app.component.html

    <div class="col-xs-12">
      <h1>Animations</h1>
      <button class="btn btn-primary" (click)="onAnimate()">Animate!</button>
      <button class="btn btn-primary" (click)="onShrink()">Shrink!</button>
      <hr>
      <div [@divState]="state" style="width:100px; height: 100px;"></div>
    </div>

app.component.ts

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

import { trigger, state, style} from '@angular/animations';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  animations: [
    trigger('divState',[
      state('normal', style({
        'background-color':'red',
        transform: 'translateX(0)'
      })),
      state('highlighted', style({
        'background-color':'blue',
        transform: 'translateX(100px)'
      }))
    ]
  )
  ]
})
export class AppComponent {
  state = 'normal';

	list = ['Milk', 'Sugar', 'Bread'];

	onAnimate() {
    this.state === 'normal' ? this.state = 'highlighted' : this.state = 'normal';
  }
	onAdd(item) {
		this.list.push(item);
	}

	onDelete(item) {
		this.list.splice(this.list.indexOf(item), 1);
	}
}

Transitions

  • We will add now a transition between the two states, in one direction and in the reverse.
  • Wit transition we define animation from one state to other and the duration as an argument of animate method (in milliseconds).

app.component.ts

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  animations: [
    trigger('divState',[
      state('normal', style({
        'background-color':'red',
        transform: 'translateX(0)'
      })),
      state('highlighted', style({
        'background-color':'blue',
        transform: 'translateX(100px)'
      })),
      transition('normal => highlighted', animate(300)),
      transition('highlighted => normal', animate(800)),
    ]
  )
  ]
})

Advanced transitions

  • One simple way to define animation in both directions is with double arrow:
      transition('normal <=> highlighted', animate(300)),
  • We can use wildcards(*) to define transition from one state to any other:
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  animations: [
    trigger('divState',[
      state('normal', style({
        'background-color':'red',
        transform: 'translateX(0)'
      })),
      state('highlighted', style({
        'background-color':'blue',
        transform: 'translateX(100px)  scale(1)'
      })),
      state('chiquito', style({
        'background-color':'green',
        transform: 'translateX(0) scale(0.5)'
      })),
      transition('normal <=> highlighted', animate(300)),
      transition('chiquito <=> *', animate(300)),
    ]
  )
  ]
})

Transition Phases

  • Now we can insert in-between animations. For instance , lets put an animation to border-radius 50px. To avoid hard step at the end we will add another animate method to transition to the end state.
  • We will pass an array of animations to the transition method
  • The first element of the array will be a starting style (background orange)
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  animations: [
    trigger('divState',[
      state('normal', style({
        'background-color':'red',
        borderRadius: '0px',
        transform: 'translateX(0)'
      })),
      state('highlighted', style({
        'background-color':'blue',
        borderRadius: '0px',
        transform: 'translateX(100px)  scale(1)'
      })),
      state('chiquito', style({
        'background-color':'green',
        transform: 'translateX(0) scale(0.5)'
      })),
      transition('normal <=> highlighted', animate(300)),
      transition('chiquito <=> *', [
        style({
          'background-color':'orange'
        }),
        animate(1000, style({
          borderRadius: '50px'
        })),
        animate(500)
      ]),
    ]
  )
  ]
})

 

The “void” state

  • Regarding the cases of adding / deleting items in a list we can animate those actions with the void state.
  • Let animate adding items by making the new items coming from the left and setting opacity from 0 to 1.
  • And lets animate deleting items by making deleted items disappearing to the right
  • We define a new trigger ‘list1’ to add into the <li> elements
  • We don’t bind any variable of state in this case to the trigger. We will use predefined internal states for lists.
  • ‘in’ state is a predefined state for elements inserted to the list
  • ‘void’ state is the state for elements to be added.

app.component.ts

    trigger('list1', [
      state('in', style({
        opacity: 1,
        transform: 'translateX(0)'
      })),
      transition('void => *', [
        style({
          opacity:0,
          transform: 'translateX(-100px)'
        }),
        animate(300)
      ]),
      transition('* => void', [
        animate(300, style({
          opacity:0,
          transform: 'translateX(100px)'
        }))
      ])
    ]),

app.component.html

      <ul class="list-group">
        <li
          class="list-group-item"
          [@list1]
          (click)="onDelete(item)"
          *ngFor="let item of list">
          {{ item }}
        </li>
      </ul>

 

Using Keyframes for animations

  • In case we want to set animations during the transition in specific steps we use keyframes.
  • In this example we will define adding item action to the list with keyframes
  • Lets define a new trigger ‘list2’ for that:
    trigger('list2', [
      state('in', style({
        opacity: 1,
        transform: 'translateX(0)'
      })),
      transition('void => *', [
        animate(1000, keyframes([
          style({
            transform:'translateX(-100px)',
            opacity:0,
            offset:0
          }),
          style({
            transform:'translateX(-50px)',
            opacity:0.5,
            offset:0.3
          }),
          style({
            transform:'translateX(-20px)',
            opacity:1,
            offset:0.8
          }),
          style({
            transform:'translateX(0px)',
            opacity:1,
            offset:1
          })
        ]))
      ]),

Grouping transitions

  • Defining multiple animate methods in a array will execute sequentially.
      transition('* => void', [
        animate(1000, style({
          color:'red'
        })),
        animate(800, style({
          opacity:0,
          transform: 'translateX(100px)'
        }))
      ])
  • But if we want multiple animate methods occurring at the same time (in parallel) we can use group method:
      transition('* => void', [
        group([
          animate(800, style({
            color:'red'
          })),
          animate(1000, style({
            opacity:0,
            transform: 'translateX(100px)'
          }))
        ])
      ])

Using animation callbacks

app.component.html

      <div
        [@divState]="state"
        (@divState.start)="animationStarted($event)"
        (@divState.done)="animationEnded($event)"
        style="width:100px; height: 100px;"
      ></div>

app.component.ts

  animationStarted(event) {
    console.log(event);
  }
  animationEnded(event) {
    console.log(event);
  }

the result is:

Captura de pantalla 2017-06-01 a las 23.14.25

Angular2 – Deploying an Angular App

Introduction

Deployment preparations and important steps

Captura de pantalla 2017-05-29 a las 11.11.09

  • Build you app for production (—prod  –aot)
  • Set a correct <base> element
  • Redirect all 404 errors to return index.html where Angular launch. Routes defined in Angular (such as /recipes)  are not configured in the server, so it will return a 404 error by default. So you will need to set up your server to redirect to index.html in these cases.

Example: Deploying to AWS S3

  • Create an Amazon account: https://console.aws.amazon.com
  • Create a bucket in S3
  • Set up index and error documents pointing to index.html to make sure Angular app is linked in every route. In S3 bucket this is done in Properties -> Static Web hosting. For deployment in php hosting create an .htaccess file:
ErrorDocument 404 http://jesidea.com/jesites/src/angular2-recipe-book/

RewriteEngine On
RewriteBase /
  • Set up permissions for that bucket for anonymous access: create a bucket policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::jesidea-ng-recipe-book/*"
        }
    ]
}
  • Build for production:

Taking care of the base folder where it will be deployed (base-href in index.html):

ng build --pro --aot --base-href http://jesidea.com/jesites/src/angular2-recipe-book/

or without taking care:

ng build --pro --aot

 

Angular2 – Using Angular Modules & Optimizing Apps

The idea behind modules

Captura de pantalla 2017-05-24 a las 15.34.54

  • Using Modules in Angular is a way of doing easier to maintain an large app.
  • We will modify our recipe book app to use multiple modules.
  • NOTE: The imports in a component or service are not related to modules, it is a JS stuff to work.

Understanding the App Module

  • Lets see the meaning of the properties of the NgModule in the app.module.ts
@NgModule({
  declarations: [
...
  imports: [
...
  providers: [ShoppingListService,RecipesService, DataStorageService, AuthService, AuthGuard],
  bootstrap: [AppComponent]
})
  • declarations : component , directives or pipes this module use.
  • imports: other modules this module use (some Angular built in modules). When we import another module what that module exports. For example FormsModule export a set of directives , and importing it we can use all of that directives.
  • providers: which service we are using in this module. Inject here will be provided for the whole app.
  • bootstrap: whats our root component.

Understanding Features Modules

  • It is about put all classes relate to a feature inside a Module, in our example Recipes Module can be a candidate.

Creating a Recipe Feature Module

  • We create a recipe/recipe.module.ts to move all recipe related components to the new feature module.
  • We also move DropdownDirective and ReactiveFormsModule into recipe.module.ts because these only been used in the recipes module.
  • BrowserModule needs to be imported only in the app.module. It contains all elements of CommonModule and additional stuff to start the app. But use CommonModule for common directives in the others modules.
  • IMPORTANT: You must not declare the same component in two different modules.

recipe/recipe.module.ts

import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { RecipeListComponent } from './recipe-list/recipe-list.component';
import { RecipeItemComponent } from './recipe-item/recipe-item.component';
import { RecipeDetailComponent } from './recipe-detail/recipe-detail.component';
import { RecipesComponent } from './recipes.component';
import { RecipeStartComponent } from './recipe-start/recipe-start.component';
import { RecipeEditComponent } from './recipe-edit/recipe-edit.component';

@NgModule({
  declarations: [
    RecipesComponent,
    RecipeListComponent,
    RecipeItemComponent,
    RecipeDetailComponent,
    RecipeStartComponent,
    RecipeEditComponent
  ],
  imports: [
    CommonModule,
    ReactiveFormsModule
  ]
})

export class RecipeModule {}

But now we have a problem with the routing.  Recipe module doesn’t have access to the AppRoutingModule, it is in the app.module and don’t travel down to the recipe.module.

We will resolve it in the next point.

NOTE: We don’t move RecipeService from app.module because it is used in others parts of the app

NOTE: Don’t forget to import CommonModule in recipe.module. It gives you access to the common directives: ngClass , ngFor, etc

Registering routes in Features Modules

  • The problem we havce is we are using router-outlet in some of the templates in recipe module but we are declaring AppRoutingModule.
  • The solution is to create recipe/recipe-routing.module.ts for get all root configuration from the parent component and add all recipe relate routes.
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/Router';
import { RecipesComponent } from './recipes.component';
import { RecipeStartComponent } from './recipe-start/recipe-start.component';
import { RecipeDetailComponent } from './recipe-detail/recipe-detail.component';
import { RecipeEditComponent } from './recipe-edit/recipe-edit.component';
import { AuthGuard } from '../auth/auth-guard.service';

const recipeRoutes: Routes = [
  { path: 'recipes', component: RecipesComponent, children: [
    { path: '', component: RecipeStartComponent},
    { path: 'new', component: RecipeEditComponent, canActivate: [AuthGuard]},
    { path: ':id', component: RecipeDetailComponent},
    { path: ':id/edit', component: RecipeEditComponent, canActivate: [AuthGuard]}
  ]},
];

@NgModule({
  imports: [
    RouterModule.forChild(recipeRoutes)
  ],
  exports: [RouterModule]

})
export class RecipeRoutingModule {
}
  • The key thing is to use forChild instead of forRoot (only in the app routing) — IMPORTANT
    RouterModule.forChild(recipeRoutes)

for link the new recipe routes.

And reference it from recipe/recipe.module.ts

...
import {RecipeRoutingModule} from './recipe-routing.module';

...

  ],
  imports: [
    CommonModule,
    ReactiveFormsModule,
    RecipeRoutingModule
  ]
  • Now we have another issue: DropdownDirective is not working in the recipe module
  • IMPORTANT: A Module can only use components in its declaration and items imported in “imports”

Understanding Shared Modules

  • We will need to move DropdowDirective to a Shared Module

Captura de pantalla 2017-05-25 a las 13.58.59

Creating a Shared Module

shared/shared.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { DropdownDirective } from './dropdown.directive';

@NgModule({
  declarations: [
    DropdownDirective
  ],
  exports: [
    CommonModule,
    DropdownDirective
  ]
})

export class SharedModule {}

Creating a Shopping List Feature Module

  • We create two new features modules: auth/auth.module.ts and shopping-list/shopping-list.module.ts.

Loading components via Selectors vs Routing

  • We cannot use app-shopping-list selector in app.component.html because it is not declared as part of the app.module.ts.
  • IMPORTANT: only use selectors declared in the same module as the template.
  • But with routes it is different. We can declare different routing files and use them anywhere if there is any place in the app where has been declared.

A Common Gotcha

  • Make sure to import FormsModule in the modules where you are using forms directives such as ng-submit,etc,

Creating the Auth Feature Module

 

Understanding Lazy Loading

Captura de pantalla 2017-05-25 a las 15.24.23

  • Lazy loading is about don’t load all the features modules at the beginning. It can happen user won’t visit some feature never so all the components related to that feature are not needed.

 

Adding Lazy Loading to the recipes module

  • We want Recipes Module to be lazily loaded.
  • The first thing to do is create a home/home.component to set it up as the entrance point of our app instead of been redirected to the recipes.
  • Then we remove RecipeModule reference from the app.module.
  • Then we modify routes in the app-routing.module.ts:
const appRoutes: Routes = [
  { path: '', component: HomeComponent},
  { path: 'recipes', loadChildren: './recipes/recipe.module#RecipeModule'}
];
  • With the “loadChildren” option we reference what module we want to load as the user visit ‘/recipes’

NOTE: in the recipe-routing.module.ts we now have:

const recipeRoutes: Routes = [
  { path: '', component: RecipesComponent, children: [
    { path: '', component: RecipeStartComponent},
    { path: 'new', component: RecipeEditComponent, canActivate: [AuthGuard]},
    { path: ':id', component: RecipeDetailComponent},
    { path: ':id/edit', component: RecipeEditComponent, canActivate: [AuthGuard]}
  ]},
];
  • We can check in the console how lazy load some “chunks” of our app since we click in recipes:

Captura de pantalla 2017-05-25 a las 16.09.42

Protecting Lazy Load Routes with canLoad

What if you want to use route protection (canActivate  to be precise) on lazily loaded routes?

You can add canActivate to the lazy loaded routes but that of course means, that you might load code which in the end can’t get accessed anyways. It would be better to check that BEFORE loading the code.

You can enforce this behavior by adding the canLoad  guard to the route which points to the lazily loaded module:

{ path: 'recipes', loadChildren: './recipes/recipes.module#RecipesModule', canLoad: [AuthGuard] }

In this example, the AuthGuard  should implement the CanLoad interface.

 

How Modules and Services work together

  • Services (LogService for example) can be injected in the Lazy Loaded modules automatically without need to specify a providers array. This is because lazy modules use the root injector which can be referenced from the whole app.

Captura de pantalla 2017-05-25 a las 16.35.20

  • Let see another use case: the lazy module has its own provider as the other modules. Then the Lazy Load Module will create a child injector and the LogService will have another instance different from the one at app level:

Captura de pantalla 2017-05-25 a las 16.40.27

  • Keep in mind : when providers array it is declared on a lazy module a child injector will be created
  • And if we want to have a LogService only for a module (Module scope) then use the providers array in the component instead of the module
  • A third use case can be use a shared module to give the LogService instance to the lazy module:

Captura de pantalla 2017-05-25 a las 16.44.09

  • But in this case a child injector will be created too, because lazy module is importing a module which has a providers array.

IMPORTANT: Don’t provide services in shared modules. Especially not , if you plan to use them in lazy loaded modules

Understanding the Core Module

  • Now we want to leave the app.module as lean as is possible. So let see what components belong to the core of the app: Header and Home.

Creating a Basic Core Module

  • We create a core folder and we put inside header and home folders.
  • We fix routes
  • We create a core.module.ts:

core/core.module.ts

import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';

import { HomeComponent } from './home/home.component';
import { HeaderComponent } from './header/header.component';

import { SharedModule } from '../shared/shared.module';

import { AppRoutingModule } from '../app-routing.module';

@NgModule({
  declarations: [
    HomeComponent,
    HeaderComponent
  ],
  imports: [
    SharedModule,
    AppRoutingModule
  ],
  exports: [
    HeaderComponent,
    AppRoutingModule
  ]
})

export class CoreModule {}

 NOTE : in the core.module we export Header component because we use the app-header selector in the app.component.html. But we don’t need to export HomeComponent because is only used in routes and in this case is visible from the whole app.

Restructuring services to use the child injector

  • We continue doing leaner the app.module.
  • This time we are moving all the services in the providers array to the core.module. These services will still be available in the whole app because the core module is loaded at the beginning si it is in the Root injector.
  • The AuthGuard service is only used in recipe module so we can define it only in that module.

core/core.module.ts

  providers: [ShoppingListService,RecipesService, DataStorageService, AuthService],

recipe/recipe.module.ts

  providers: [AuthGuard]

NOTE: Adding the AuthGuard service only to the recipe module we are improving the speed of the app in the bootstrap as Recipe module is lazy loaded.

Using Ahead-of-time compilation

  • This is NOT related to compilation from Typescript to Javascript.
  • This is about Angular needs to compile HTML templates to convert them to Js objects
  • This step can be done before the app is released (without loosing the fact of some templates are dynamic).

Captura de pantalla 2017-05-25 a las 17.58.01

  • Using AoT has advantages:
    • Faster startup since Parsing and Compilation doesn’t happen in the browser
    • Templates get checked during development: all the console errors will trigger in the build process not in the user browser.
    • Smaller file size as unused features can be stripped out and the Compiler itself isn’t shipped.

 

How to use AoT compilation with the CLI

  • The CLI command to run on terminal is:
ng build --pro --aot
  • The –pro flag is top minify
  • The –aot flag is for Ahead of Time compilation
  • Some errors can be triggered with this command you should resolve to get it completed.
  • Once the command is completed a dist folder will be generated and you can check size of the app files: main.js and vendor.js and compare it with the sizes in the chrome console on development time.

Preloading Lazy Loading routes

  • We can preload all the lazy loading modules , not at the time the app start but during user is using the app.
  • In the app-routing.module.ts, we need to specify the preloadingStrategy:
@NgModule({
  imports: [
    RouterModule.forRoot(appRoutes, {preloadingStrategy: PreloadAllModules})
  ],
  exports: [RouterModule]
})
  • Then in debug console –> network, we can see how chunk file is loaded after the main and vendor js files

Captura de pantalla 2017-05-26 a las 10.03.15

 

Angular2 – Authentication and Route protection

Module introduction

  • We want allow only logged in users to modify recipes

How Authentication works in SPAs

  • In SPAs authentication is managed by tokens

Captura de pantalla 2017-05-23 a las 19.14.33

More about jWT

Creating a signup page and route

  • We create a Sign up form page and his route “/signup”. All associated to a auth/signup.component

Setting up the firebase SDK

  • In firebase console we need to enable basic authentication in the “Authentication” menu option.
  • We need to install firebase npm package
sudo npm install firebase --save

 NOTE: In a normal backend you can have a available endpoint to retrieve a valid token but firebase hasn’t.

  • Create an auth/auth.service.ts with a signupUser method
  • Initialize firebase credentials in a app level: app.component.ts
...
import * as firebase from 'firebase';
...
export class AppComponent implements OnInit{
...
  ngOnInit() {
    firebase.initializeApp({
      apiKey: "AIzaSyCqHQQ7gtNxkERmJzTBDfkn_Sq_2LeY9qc",
      authDomain: "ng-recipe-book-5333b.firebaseapp.com"
    });
  }

 

Signing Users Up

auth.service.ts

import { Injectable } from '@angular/core';
import * as firebase from 'firebase';


@Injectable()
export class AuthService {

  signupUser(mail: string, password:string) {

    firebase.auth().createUserWithEmailAndPassword(mail, password)
    .catch(
      error => console.log(error)
    );

  }
}

signup.component.ts

  onSignup(form: NgForm) {
    const mail = form.value.email;
    const password = form.value.password;

    this.authService.signupUser(mail, password);

  }

Signing Users In

  • We create /signin route and Signin component calling signinUser method on auth service

auth.service.ts

  signinUser(mail: string, password:string) {

    firebase.auth().signInWithEmailAndPassword(mail, password)
    .then(
      response => console.log(response)
    )
    .catch(
      error => console.log(error)
    );

  }
  • Firebase store i the browser information of logged user. You can check it in console -> Application –> Local Storage
  • There it is the accessToken we will using for make future requests.

Requiring a Token (in the Backend)

  • For requiring authentication in the firebase (our backend) we need to set up database rules in the firebase console:
{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

Sending the token

  • Now for making Fetch Data and Save Data action we need to append accessToken of logged users in the http requests.

auth.service.ts

  token: string;

  getToken() {
    firebase.auth().currentUser.getToken()
    .then(
      (token:string) => {
        this.token = token;
      }
    );
    return this.token;
  }
  signinUser(mail: string, password:string) {

    firebase.auth().signInWithEmailAndPassword(mail, password)
    .then(
      response => {
        firebase.auth().currentUser.getToken()
        .then(
          (token:string) => {
            this.token = token;
          }
        )
      }
    )
    .catch(
      error => console.log(error)
    );

  }

data-storage.service.ts

  • We append token in the url of the requests
  storeRecipes() {
    const token = this.authService.getToken();
      return this.http.put('https://ng-recipe-book-5333b.firebaseio.com/recipes.json?auth='+token,
    this.recipesService.getRecipes());
  }

Checking and using Authentication status

  • We want to show dropdown menu at the top right only for authenticated users

auth.service.ts

  isAuthenticated() {
      return this.token != null;
  }

header.component.ts

  constructor(private dataStorageService: DataStorageService,
    private recipesService: RecipesService,
    private authService: AuthService) { }

header.component.html

      <ul class="nav navbar-nav navbar-right">
        <ng-template [ngIf]="!authService.isAuthenticated()">
          <li><a href="/signin">Sign in</a></li>
          <li><a href="/signup">Sign up</a></li>
        </ng-template>
        <li class="dropdown" appDropdown *ngIf="authService.isAuthenticated()">
          <a style="cursor: pointer;" class="dropdown-toggle" role="button">Manage <span class="caret"></span></a>
          <ul class="dropdown-menu">
            <li><a style="cursor: pointer;" (click)="onSaveData()">Save Data</a></li>
            <li><a style="cursor: pointer;" (click)="onFetchData()">Fetch Data</a></li>
          </ul>
        </li>

Adding a Logout button

auth.service.ts

  logout() {
    firebase.auth().signOut();
    this.token = null;
  }

Route protection and redirection example

  • Protect some routes for only logged in users

auth/auth-guard.service.ts

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot , RouterStateSnapshot} from '@angular/router';
import { AuthService } from './auth.service';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService) {}
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return this.authService.isAuthenticated();
  }

}

app-routing.module.ts

...    
    { path: 'new', component: RecipeEditComponent, canActivate: [AuthGuard]},
    { path: ':id', component: RecipeDetailComponent},
    { path: ':id/edit', component: RecipeEditComponent, canActivate: [AuthGuard]}
...
  • After sign in we redirect to main page:

auth.service.ts

  signinUser(mail: string, password:string) {

    firebase.auth().signInWithEmailAndPassword(mail, password)
    .then(
      response => {
        this.router.navigate(['/']);
        firebase.auth().currentUser.getToken()
        .then(
          (token:string) => {
            this.token = token;
          }
        )
      }
    )
    .catch(
      error => console.log(error)
    );

  }

Wrap up

Possible improvements

 

Angular2 – Making Http Requests

Introduction

  • Traditional Web apps against SPAs

Example App & Backend setup

  • We use firebase to make a test database: https://ng-http-cb90d.firebaseio.com/
  • NOTE: Keep in mind set database rules to get free access:
{
  "rules": {
    ".read": "true",
    ".write": "true"
  }
}

 

Sending requests (example POST)

  • With the Angular Http class we can create Observables.
  • But the request is not sent until someone subscribe to that observable.
  • In the next example, we click on a button to subscribe to the POST method which trigger the POST request.

server.service.ts

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

@Injectable()
export class ServerService {
  constructor(private http: Http) {}

  storeServers(servers: any[]) {
    return this.http.post('https://ng-http-cb90d.firebaseio.com/data.json', servers);
  }
}

app.module.ts

providers: [ServerService],

app.component.ts

  constructor(private serverService: ServerService) {}
  
  onSave() {
    this.serverService.storeServers(this.servers). subscribe (
      (response)=> {
        console.log(response);
      },
      (error) => console.log(error)

    );
  }

app.component.html

      <button type="button" class="btn btn-primary" (click)="onSave()">Save servers</button>

Adjusting request headers

  • We can add headers with the Headers class:

server.service.ts

  storeServers(servers: any[]) {

    const headers = new Headers({'Content-type': 'application/json'});
    return this.http.post('https://ng-http-cb90d.firebaseio.com/data.json', servers, { headers: headers });
  }

Sending GET requests

app.component.ts

  onGet() {
    this.serverService.getServers().subscribe(
      (response: Response) => {
        const data = response.json();
        console.log(data);
      },
      (error) => console.log(error)
    );
  }

server.service.ts

  getServers() {
    return this.http.get('https://ng-http-cb90d.firebaseio.com/data.json');
  }

 

Sending a PUT requests

  • In Firebase the POST methods add items to the database, but with the PUT method we can update the whole database. (This is depending of the backend database)
  storeServers(servers: any[]) {
    const headers = new Headers({'Content-type': 'application/json'});
    return this.http.put('https://ng-http-cb90d.firebaseio.com/data.json', servers, { headers: headers });
    // return this.http.post('https://ng-http-cb90d.firebaseio.com/data.json', servers, { headers: headers });
  }

Transform response easily with observable operators (map())

  • Lets extract the data from the server response to render it properly.
  • We can use map() (import rxjs/Rx is needed) for transform response from the server. What map method does is transform an observable in another observable.
  • We will make this transformation in the service which is much better (a centralized place):
  getServers() {
    return this.http.get('https://ng-http-cb90d.firebaseio.com/data.json')
    .map(
      (response: Response)=> {
        const data = response.json();
        return data;
      }
    );
  }

Using the returned data

server.service.ts

getServers() {
    return this.http.get('https://ng-http-cb90d.firebaseio.com/data.json')
    .map(
      (response: Response)=> {
        const data = response.json();
        for(let server of data) {
          server.name = 'FETCHED_' + server.name;
        }
        return data;
      }
    );
  }

app.component.ts

    this.serverService.getServers().subscribe(
      (servers: any[]) => {
        // const data = response.json();
        console.log(servers);
        this.servers = servers;
      },
      (error) => console.log(error)
    );

 

Catching Http errors

  • We want to catch request errors and print more friendly messages to the chrome console.
  • We force and error removing .json at the end of the url.

server.service.ts

  return this.http.get('https://ng-http-cb90d.firebaseio.com/data')
...
    .catch(
      (error: Response) => {
        console.log(error);
        return Observable.throw('Something went wrong');
        // return Observable.throw(error);

      }
    );

app.component.ts

  onGet() {
    this.serverService.getServers().subscribe(
      (servers: any[]) => {
        // const data = response.json();
        console.log(servers);
        this.servers = servers;
      },
      (error) => console.log(error)
    );
  }

Using the async pipe with Http requests

  • We explain here how to use async to render values depending of http requests.

server.service.ts

  getAppName() {
    return this.http.get('https://ng-http-cb90d.firebaseio.com/appName.json')
    .map(
      (response: Response) => {
        return response.json();
      }
    );

  }

app.component.ts

appName = this.serverService.getAppName();

app.component.html

<h1>{{appName | async}}</h1>