Angular

jon rojí

github.com/jroji

1joroji@gmail.com

@jnroji

CONTENT

  • Introducción
  • Typescript + ECMAScript
  • Gestión de dependencias
  • Componentes
  • Sintaxis
  • Ciclo de vida de un componente
  • Flujo de comunicación entre componentes
  • RxJs
  • Modularización
    Router
  • Forms
  • Construcción
  • Unit testing en Angular 2

Background

Angular 1 is one of the most used 

frameworks in the industry until now

 

  Main advantages

    > Dependency injection

    > Scopes

    > Two-way data binding

  Main disadvantages

    > Reusability low level

    > Performance

    > Strong hierarchy structure

Evolution

Due to the comments of the community and the new tools provided by the browsers, the Angular Team decided to full rethink the new version of the framework

Scopes

Two-way data binding

ES5

Directives, services, factories 

SCOPE VALUES

COMPONENT CLASS PROPERTIES

angular.module('scopeExample', [])
.controller('MyController', ['$scope', function($scope) {
  $scope.username = 'World';

  $scope.sayHello = function() {
    $scope.greeting = 'Hello ' + $scope.username + '!';
  };
}]);
@Component({selector: 'greet', template: 'Hello {{name}}!'})
class Greet {
  name: string = 'World';
}

aNGULAR IS A COMPONENT-BASED SPA FRAMEWORK

custom elements

shadow dom

templates

class AppDrawer extends HTMLElement {...}

window.customElements.define('app-drawer', AppDrawer);

custom elements

shadow dom

templates

const header = document.createElement('header');
const shadowRoot = header.attachShadow({mode: 'open'});

custom elements

shadow dom

templates

<template id="sdtemplate">
  <style>
    p { color: orange; }
  </style>
  <p>Hello</p>
</template>

COMPONENTS: VISUAL AND LOGICAL COMPONENTS TO CONSTRUCT THE VIEWS

@Component({
  selector: 'my-component',
  template: `<div>
                Hello my name is {{name}}. 
                <button (click)="sayMyName()">Say my name</button>
            </div>`
})

export class MyComponent {
  constructor() {
    this.name = 'Max'
  }
  sayMyName() {
    console.log('My name is', this.name)
  }
}

browser support

FULL FRAMEWORK

COMPONENTS!

TYPESCRIPT!

JIT / AOT!

DEPENDENCY INJECTION!​

Routing

Lazy Load

HTTP

much more!

ECMASCRIPT6

let / const

let declares a local scopped var

The scope of a variable defined with var is function scope or declared outside any function, global.
The scope of a variable defined with let is block scope.

 

const declares an "inmutable" var

let / const

//ES5
(function() {
    console.log(x); // x not defined yet.
    if(true) {
        var x = "hello world";
    }
    console.log(x); 
    // Paints "hello world", because "var" make it global
})();

//ES6
(function() {
    if(true) {
        let x = "hello world";
    }
    console.log(x); 
    //Error, because x has been defined inside the if scope
})();

fat arrows

two main purposes: more concise syntax and sharing lexical this with the parent scope.

// BEFORE
var self = this;
function () { return self.quantity + 1; }

//  AFTER
() => { return this.quantity + 1; }
() => this.quantity + 1

Default values

We can define default values for our parameters in functions definition

// ES5
function test(x, y) {
    if(x == null) {
        x = 5;
    }
    ...
}


// ES6
function test(x = 5, y) {
    ...
}

STRINGS INTERPOLATION

With ES6, we don't need anymore the '' + '' syntax. We can use ${myVar} inside the code

// ES5
var x = 'Mr.' + name;

// ES6
let x = `Mr.${name}`;

And it supports multiline!

Object properties

We can define functions and properties easier in a object (object-like syntax)

// ES5
obj = {
    foo: function (a, b) {
        …
    },
    bar: function (x, y) {
        …
    }
};

// ES6
obj = {
    foo(a, b) {
        …
    },
    bar(x, y) {
        …
    }
};

classes (with ts)

scoping (ts)

private

public

protected

inheritance

constructor

getter / setter

inmutable prop

JS MODULES

export interface StringValidator {
    isAcceptable(s: string): boolean;
}
import { StringValidator } from "./StringValidator";
// import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";
// import * as validator from "./StringValidator";


let myValidator = new StringValidator();

StringValidator.ts

App.ts

Typescript

open source JAVASCRIPT SUPERSET

by microsoft

why typescript?

 

JAVASCRIPT SUPERSET

STATIC TYPING

ECMASCRIPT 6 TRANSPILER

OBJECT ORIENTED

ERROR DETECTION BEFORE RUNTIME

MORE ROBUST CODE

BACKENDERS TECHNOLOGIES LIKE

SAME SYNTAX

strong typing

don't be afraid

why typescript?

 

JAVASCRIPT SUPERSET

STATIC TYPING

ECMASCRIPT 6 TRANSPILER

OBJECT ORIENTED

ERROR DETECTION BEFORE RUNTIME

MORE ROBUST CODE

BACKENDERS TECHNOLOGIES LIKE

OOP

ES6

why typescript?

 

JAVASCRIPT SUPERSET

STATIC TYPING

ECMASCRIPT 6 TRANSPILER

OBJECT ORIENTED

ERROR DETECTION BEFORE RUNTIME

MORE ROBUST CODE

BACKENDERS TECHNOLOGIES LIKE

ts  > js

Boolean

number

STRING

array

tuple

enum

any

both?

let notSure: number|string;

functions

lets use types in the input/output parameters

 

readonly props

interfaces

Specifies methods and properties without implementation. Usefull for require a concrete structure on a method

classes mixins

We can create a bigger class ussing more little classes. Usefull for a cleaner structure and cleaner code, even to improve the tests organization and execution

decorators

Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.

function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}
@sealed
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

tsconfig.json

configuration file in our project

{
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "dist"
  },
  "files": [
    "src/Person.ts"
  ]
}

is just me?

why npm

dependency management

our internal dependencies

biggest repository of modules

npm packages

  • version Must match version exactly

  • '>'version Must be greater than version

  • '>'=version etc

  • '<'version

  • '<'=version

  • ~version "Approximately equivalent to version"

  • ^version "Compatible with version"

  • 1.2.x 1.2.0, 1.2.1, etc., but not 1.3.0

  • "" (just an empty string) Same as *

  • version1 - version2 Same as >=version1 <=version2.

  • range1 || range2 Passes if either range1 or range2 are satisfied.

  • tag A specific version tagged and published as tag See npm-tag

starting with npm

$ mkdir npm-proj && cd npm-proj

$ npm init

$ npm install typescript
// OR
$ npm install typescript@2.2.0
// OR
$ npm install --save typescript
// OR
$ npm install --save-dev typescript
// OR
$ npm install -g typescript

let's try!

components

COMPONENTS: VISUAL AND LOGICAL COMPONENTS TO CONSTRUCT THE VIEWS

@Component({
  selector: 'my-component',
  template: `<div>
                Hello my name is {{name}}. 
                <button (click)="sayMyName()">Say my name</button>
            </div>`
})

export class MyComponent {
  constructor() {
    this.name = 'Max'
  }
  sayMyName() {
    console.log('My name is', this.name)
  }
}

dissection of a component

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

@Component({
  selector: 'my-component',
  template: `<div>
                Hello my name is {{name}}
            </div>`,
  providers: [ MyService ]
})
export class MyComponent {
  constructor() {
    this.name = 'Max'
  }
  sayMyName() {
    console.log('My name is', this.name)
  }
}

Import Component decorator from angular/core

Specify properties

selector, template...

Component controller

Component functions

use it

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

@Component({
  selector: 'my-component',
  template: `<div>
                Hello my name is {{name}}
            </div>`
})
export class MyComponent {
  constructor() {
    this.name = 'Max'
  }
  sayMyName() {
    console.log('My name is', this.name)
  }
}
<body>
    <div>
        <my-component></my-component>
    </div>
</body>

properties

selector

specifies the custom tag for our component. It will be registered in the DOM when the <my-component> tag is used

  selector: 'my-component'

template

specifies the template of the component that will be rendered in the browser. A external template can be referenced by templateUrl, that will be compiled in execution / compilation time, depending on build system

  template: '<h1>Hello</h1>',
  templateUrl: './myComponent.html'

properties

styles

specifies the styles applyed to this component. External css files can be referenced using stylesUrl. These CSS are encapsulated by default with "emulated" strategy. The encapsulation strategy can be configured with the "encapsulation" property and can have any of the following values:

 

  • Emulated 
  • Native
  • None
  styles: `.test { color: red; }`,
  stylesUrl: './myComponent.css'
  import ViewEncapsulation from @angular/core

   ...

  encapsulation: ViewEncapsulation.native

LIFE CYCLE

export class PeekABoo implements OnInit {
  constructor(private logger: LoggerService) { }

  // implement OnInit's `ngOnInit` method
  ngOnInit() { this.logIt(`OnInit`); }

  logIt(msg: string) {
    this.logger.log(`#${nextId++} ${msg}`);
  }
}

life cycle

ngOnInit    
Initialize the directive/component after Angular first displays the data-bound properties and sets the directive/component's input properties.

ngAfterContentInit
Respond after Angular projects external content into the component's view.

life cycle

ngAfterViewInit     
Respond after Angular initializes the component's views and child views.

ngOnDestroy

Cleanup just before Angular destroys the directive/component. Unsubscribe observables and detach event handlers to avoid memory leaks.

data flow

DATA / CLASS

my-component

binding

events

Angular component

quick syntax

<img [src] = "heroImageUrl">

Access to Angular context with [ ] 

<h1> {{ title }} </h1>

Render information with {{ }}

<button (click)="myFunc()">

Listen to events with ( ) 

data flow

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

@Component({
  selector: 'hero-child',
  template: `<p>I am at your service, {{masterName}}.</p>`
})

export class HeroChildComponent {
  @Input() name: string;
  @Input('master') masterName: string;
}

input

define properties that can be setted by the parent

the Inputs can have an input alias

<hero-child [master]="heroName"></hero-child>

data flow

import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'my-voter',
  template: `<button (click)="vote(true)">Agree</button>`
})

export class VoterComponent {
  @Output() onVoted = new EventEmitter<boolean>();
  vote(agreed: boolean) {
    this.onVoted.emit(agreed);
  }
}

OUTPUt

define and send events to child-parent communication

<my-voter (onVoted)="myFunction"></my-voter>

webpack

angular-cli

$ npm install -g @angular/cli
$ ng new test-app
$ cd test-app
$ ng serve
$ ng g component my-new-component

let's try!

templating

templating

data binding

Basic way of data binding.  We can use expressions or

call controller class methods

<p>The sum of 1 + 1 is {{1+1}}</p>


<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p>

templating

data binding

Things that we can't do

  • assignments (=, +=, -=, ...)
  • new
  • chaining expressions with ; or ,
  • increment and decrement operators (++ and --)
  • no support for the bitwise operators | and &
  • new template expression operators, such as | and ?.

templating

DATA BINDING

 

prop="text"

prop has "text" value

 

[PROP]="TEXT"

prop has context's text property value

templating

DATA BINDING

 

QUICK EXECUTION

don`t mess with the app performance

NO VISIBLE SIDE EFFECTS

expression doesn't change app state

SIMPLICITY

put the logic in the component if is too complex

 

EVENTS

<button (click)="readRainbow($event)">	

NOT JUST CLICK

We can listen to ANY event created by the browser or developers on children

components

templating

DATA BINDING

 

 

 

<input [(ngModel)]="name" >
<input [ngModel]="name" (ngModelChange)="name=$event">

equals to

templating

conditional class binding

 

 

<div [class.my-class]="isActive"> Hello </div>

<div [ngClass]="{active: isActive}"> Hello </div>
setCurrentClasses() {
  // CSS classes: added/removed per current state of component properties
  this.currentClasses =  {
    saveable: this.canSave,
    modified: !this.isUnchanged,
    special:  this.isSpecial
  };
}

<div [ngClass]="currentClasses"></div>

or as an object of classes

on template

templating

conditional style binding

 

 

<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" >
  This div is x-large or smaller.
</div>
setCurrentStyles() {
  // CSS styles: set per current state of component properties
  this.currentStyles = {
    'font-style':  this.canSave      ? 'italic' : 'normal',
    'font-weight': !this.isUnchanged ? 'bold'   : 'normal',
    'font-size':   this.isSpecial    ? '24px'   : '12px'
  };
}

or as an object of classes

on template

templating

STRUCTURAL DIRECTIVES

 

<for-example *ngFor="let episode of episodes" [episode]="episode">
    {{episode.title}}
</for-example>

*ngIf 
Hiding an element is quite different from removing an element with NgIf.

<div *ngIf="errorCount > 0" class="error">
  {{errorCount}} errors detected
</div>

<hero-detail *ngIf="isActive"></hero-detail>
*ngFor

templating

STRUCTURAL DIRECTIVES

 

*ngSwitch  
 

<div [ngSwitch]="hero?.emotion">
  <happy-hero    *ngSwitchCase="'happy'"    [hero]="hero"></happy-hero>
  <sad-hero      *ngSwitchCase="'sad'"      [hero]="hero"></sad-hero>
  <confused-hero *ngSwitchCase="'confused'" [hero]="hero"></confused-hero>
  <unknown-hero  *ngSwitchDefault           [hero]="hero"></unknown-hero>
</div>

injectables

injectables: declares services or values in order to be used by components

import { HEROES } from './mock-heroes';

@Injectable()
export class HeroService {
  getHeroes(): Hero[] {
    return HEROES;
  }
}
// Component
constructor(private heroService: HeroService) { }

Angular stores the instance of the injectable service

Simplify and reduce the necessary information that a dependency needs in order to simplify our component

 

import { Component } from '@angular/core';
import { Hamburger } from '../services/hamburger';
@Component({
  selector: 'app',
  template: `Bun Type: {{ bunType }}`
})
export class App {
  bunType: string;
  constructor(h: Hamburger) {
    this.bunType = h.bun.type;
  }
}
providers: [{ provide:Logger, useClass: TimestampLogger}]

Can use an alias in order to update the provider avoiding change it in the application

We can use the @Optional decorator in order to make that dependency not critical 

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

class AnotherService{
    constructor(@Optional() private logger: Logger) {
      if (this.logger) {
        this.logger.log("I can log!!");
      }
    }
}

Very usefull in order to use dev dependencies without modifying code

Using an injectable

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

@Injectable()
export class ChatService {
  private messages: Array<Object>;

  getMessages(): Array<Object> {
    return this.messages;
  }
}
import { ChatService } from '../chat.service.ts';

@Component({
  selector: <chat-component></chat-component>,
  template: '...'
})
export class ChatComponent {
  private messages: Array<Object>;
  
  constructor(private chatService:ChatService) {
    this.messages = this.chatService.getMessages();
  }
}

HTTP SERVICE

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

  constructor (private http: HttpClient) {}

  getHeroes (): Promise<Hero[]> {
    return this.http.get(this.heroesUrl)
                  .toPromise();
  }

  // OR WITH OBSERVABLES

  getHeroes (): Observable<Hero[]> {
    return this.http.get(this.heroesUrl)
  }

Observable is preferred over Promise because it provides the features of Promise and more. Doesn't matter if you want to handle 0, 1, or multiple events.

 

 

Observable also has the advantage over Promise to be cancelable

An Observable emits items or sends notifications to its observers when the observed item is modified

import { Observable } from 'rxjs/Observable';


// Create the observable
const data = new Observable(observer => {
  setTimeout(function() {
    observer.next(40);
  }, 2000
});

....

// Subscribe the observable
data.subscribe(value => {
    console.log(value);
});

Modules

module: declaration of components, directives, providers and schemas

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

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

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

Every Angular app has at least one module, the root module, conventionally named AppModule.

 

 

declarations - the view classes that belong to this module. Angular has three kinds of view classes: components, directives, and pipes.

 

 

exports - the subset of declarations that should be visible and usable in the component templates of other modules.

 

 

imports - other modules whose exported classes are needed by component templates declared in this module.

 

 

 

providers - creators of services that this module contributes to the global collection of services; they become accessible in all parts of the app.

 

FOR EACH PROVIDER, A NEW INSTANCE OF THE DEPENDENCY IS CREATED

 

 

bootstrap - the main application view, called the root component, that hosts all other app views. Only the root module should set this bootstrap property. (Solo para el mainmodule).

 

 

providers - creators of services that this module contributes to the global collection of services; they become accessible in all parts of the app.

 

 

bootstrap - the main application view, called the root component, that hosts all other app views. Only the root module should set this bootstrap property. (Solo para el mainmodule).

root module

feature module

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

Modules imported dinamically or statically for other sections or functionalities

let's try!

ROUTER

background

Multiple pages

 

Load same resources in every page

section

resources

section

resources

section

resources

background

One page

 

Brings all resources

section 1

resources

section 2

section 3

background

One page

 

Brings CRITICAL resources

 

Brings resources on demand

 

section 1

resources

section 2

section 3

Angular router

The first and more important step!

 

Add the base tag to your index.html in order to tell your angular application where to look for static resources

<base href="/">

Angular router

Second! lets create our routes object

We can import the Routes interface in order to define the routes. The order matters!

import { RouterModule, Routes } from '@angular/router';

const appRoutes: Routes = [
  { path: 'crisis-center', component: CrisisListComponent },
  { path: 'hero/:id',      component: HeroDetailComponent },
  {
    path: 'heroes',
    component: HeroListComponent,
    data: { title: 'Heroes List' }
  },
  { path: '',
    redirectTo: '/heroes',
    pathMatch: 'full'
  },
  { path: '**', component: PageNotFoundComponent }
];

 

 

  • Each Route maps a URL path to a component. There are no leading slashes in the path. The router parses and builds the final URL for you, allowing you to use both relative and absolute paths when navigating between application views.

  • The :id in the first route is a token for a route parameter. In a URL such as /hero/42, "42" is the value of the id parameter. The corresponding HeroDetailComponent will use that value to find and present the hero whose id is 42.


  { path: 'crisis-center', component: CrisisListComponent },
  { path: 'hero/:id',      component: HeroDetailComponent },
 
  • The data property is a place to store arbitrary data associated with this specific route. The data property is accessible within each activated route. Use it to store items such as page titles, breadcrumb text, and other read-only, static data. 

 

  • The empty path represents the default path for the application, the place to go when the path in the URL is empty, as it typically is at the start.
  {
    path: 'heroes',
    component: HeroListComponent,
    data: { title: 'Heroes List' }
  },
  { path: '',
    redirectTo: '/heroes',
    pathMatch: 'full'
  },
  { path: '**', component: PageNotFoundComponent }
  • We can use a children object in order to add new subroutes. Those routes will be displayed in the "father" component <router-outlet></router-outlet>
  {
    path: 'heroes',
    component: HeroListComponent,
    data: { title: 'Heroes List' },
    children: {
        path: 'Test',
        component: TestComponent
    }
  }

Angular router

The selected router will be displayed on the <router-outlet> tag of the "bootstrap component" in the root component, or the father component if we are using "children" routes

<router-outlet></router-outlet>

Angular router

And finally, we add the routes object to our module, and we can define a "bootstrap" for contain our components

const routes: Routes = [
    { path: 'component-one', component: ComponentOne },
    { path: 'component-two', component: ComponentTwo }
];
export const routing = RouterModule.forRoot(routes);


@NgModule({
    ...
    imports: [
        BrowserModule,
        routing
    ],
    bootstrap: [ AppComponent ]
    ...
}

Angular router

Now, we can use routerLink directive in our a elements to set the current url

<a routerLink="/heroes" routerLinkActive="active">Heroes</a>

We can use the routerLinkActive to tell Angular to add an active class by himself

LET'S THINK IN MODULES

Module

cmp

cmp

cmp

ROUTES

INJ

Module

cmp

cmp

cmp

ROUTES

INJ

Module 1

Module 2

Module 3

javascript presents

LAZY LOAD

Do less. Be lazy

Lazy load

$ ng g module lazy
$ ng g component lazy-info

Lets create a new module in our application and a new component for the new module

Lazy load

  {
    path: 'lazy',
    loadChildren: './lazy/lazy.module#LazyModule'
  },

Modify the app.routes.ts in order to add a new route with "loadChildren"

Remove lazy-info component from the app.module declarations

 

Lazy load


import {Routes} from "@angular/router";
import {LazyInfoComponent} from "../lazy-info/lazy-info.component";

export const lazyRoutes: Routes = [
  {
    path: '',
    component: LazyInfoComponent
  }
]

Now lets create a lazy.routes.ts in the lazy module directory with the relative routes

Lazy load

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {LazyInfoComponent} from "../lazy-info/lazy-info.component";
import {RouterModule} from "@angular/router";
import {lazyRoutes} from "./lazy.routes";

@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild(lazyRoutes)
  ],
  declarations: [
    LazyInfoComponent
  ]
})
export class LazyModule { }

And import them into our new module. But CAREFUL!, now we will use the forChild function

preloading modules

With the previous steps, we will have an application loading only the critical resources in the first load, and the feature modules loading by request

preloading modules


@NgModule({
  bootstrap: [TestComponent],
  imports: [RouterModule.forRoot(ROUTES, 
    {preloadingStrategy: PreloadAllModules})]
})
class TestModule {}

We can use preloadingStrategy in order to preload modules

preloading modules

We can create custom preload strategies!

export class PreloadSelectedModulesList implements PreloadingStrategy {
  preload(route: Route, load: Function): Observable<any> {
    return route.data && route.data.preload ? load() : of(null);
  }
}
[
  {
    path: 'moduleA',
    loadChildren: './moduleA.module',
    data: {preload: true}
  }
]

guards

Any user can navigate wherever he wants by our web and our modules. But maybe

 

  • Maybe the user must login (authenticate) first.
  • Maybe you should fetch some data before you display the target component.
  • You might want to save pending changes before leaving a component.

guards

 

In order to handle those scenarios, we can use guards. These mechanism allow us to "do things" before the state loads, such as prevent the module load based on the user role, or get some data before the component initialize

guards

CanActivate executes to mediate in a navigation. Can prevent the navigation if the guard returns false.

 

CanDeactivate similar to the previous one but executes on the "away" navigation from state. Useful for unsuscribe observables, or stop watching certain vars

guards

Resolve executes a function in order to get some data, required by the loading component.

 

 

CanLoad a executes a function in order to decide if the asynchronous module should be loaded or not.

guards

import { Injectable }     from '@angular/core';
import { CanActivate }    from '@angular/router';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate() {
    console.log('AuthGuard#canActivate called');
    return true;
  }
}
import { AuthGuard }                from '../auth-guard.service';

const adminRoutes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [AuthGuard]
  }
]

CanActivate

guards

import { Injectable }             from '@angular/core';
import { Router, Resolve, RouterStateSnapshot,
         ActivatedRouteSnapshot } from '@angular/router';
import { Crisis, CrisisService } from './crisis.service';

@Injectable()
export class CrisisDetailResolver implements Resolve<Crisis> {
  constructor(private cs: CrisisService, private router: Router) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Crisis> {
    let id = route.params['id'];
    // get data from a service
    return this.cs.getCrisis(id).then(crisis => {
      if (crisis) {
        return crisis;
      } else { // id not found
        this.router.navigate(['/crisis-center']);
        return null;
      }
    });
  }
}

Resolver

guards

// On my component

ngOnInit() {
  this.route.data
    .subscribe((data: { crisis: Crisis }) => {
      this.editName = data.crisis.name;
      this.crisis = data.crisis;
    });
}

Resolver

NGRX/STORE

NGRX/STORE

@ngrx/store is a controlled state container designed to help write performant, consistent applications on top of Angular. Core tenets:

  • State is a single immutable data structure
  • Actions describe state changes
  • Pure functions called reducers take previous slices of state and the next action to compute the new state
  • State accessed with the Store, an observable of state and an observer of actions

NGRX/STORE

$ ng new store-app
npm install @ngrx/store --save

NGRX/STORE

//main.state.ts
export interface State {
  counter: number;
};

export const intitialState: State = {
  counter: 10
};

Inside of the state folder let’s create a new file

NGRX/STORE

import {ActionReducer, Action} from "@ngrx/store";
import {State, intitialState} from "../state/main-state";

export const mainStoreReducer: ActionReducer<State> = (state = intitialState, action: Action) => {
    switch (action.type) {
      case 'INCREMENT': {
        return {
          counter: state.counter + 1
        }
      }
      case 'EVENT_FROM_EFFECT': {
        return {
          counter: 4
        }
      }
      default: {
        return state;
      }
    }
  };

The reducer is the thing that catches and handles the actions that are dispatched from your smart components (or from your action reducers), and it is also the thing that actually resets the state to a new, modified state

NGRX/STORE

import {ActionReducer, Action} from "@ngrx/store";
import {State, intitialState} from "../state/main-state";

export const mainStoreReducer: ActionReducer<State> = (state = intitialState, action: Action) => {
    switch (action.type) {
      case 'INCREMENT': {
        return {
          counter: state.counter + 1
        }
      }
      case 'EVENT_FROM_EFFECT': {
        return {
          counter: 4
        }
      }
      default: {
        return state;
      }
    }
  };

The reducer is the thing that catches and handles the actions that are dispatched from your smart components (or from your action reducers), and it is also the thing that actually resets the state to a new, modified state

NGRX/STORE

//app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { mainStoreReducer } from "./state-management/reducers/main.reducer";
import { StoreModule } from "@ngrx/store";

const reducers = { counter: mainStoreReducer };

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

NGRX/STORE

import { Component } from '@angular/core';
import { Store } from "@ngrx/store";
import { State } from "./state-management/state/main.state";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app works!';
  constructor (private store:Store<State>) {
    console.log('We have a store! ' + store);
  }
}

NGRX/STORE

import { Component } from '@angular/core';
import { Store } from "@ngrx/store";
import { State } from "./state-management/state/main.state";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app works!';
  data = '';
  constructor (private store:Store<State>) {
    store.select('counter')
      .subscribe( (data:State )=> {
        this.data = 'data is' + data;
      });

  }
}

Reading data

NGRX/STORE

import { Component } from '@angular/core';
import { Store } from "@ngrx/store";
import { State } from "./state-management/state/main.state";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app works!';
  data = '';
  constructor (private store:Store<State>) {
    store.select('mainStoreReducer')
      .subscribe( (data:State )=> {
        this.data = 'data is ' + data;
      });
  }

  public increment() {
    this.store.dispatch({ type: 'INCREMENT' });
  }
}

Calling reducer actions

NGRX/STORE

//app.component.html
<h1>
  {{title}}
</h1>
<h2>
  {{data}}
</h2>
<button (click)="increment()"> + 1 </button>

Angular + firebase

firebase database

"Firebase provides a realtime database and backend as a service. The service provides application developers an API that allows application data to be synchronized across clients and stored on Firebase's cloud."

firebase

firebase

// environment/environment.ts
export const environment = {
  production: false,
  firebase: {
    apiKey: '<your-key>',
    authDomain: '<your-project-authdomain>',
    databaseURL: '<your-database-URL>',
    projectId: '<your-project-id>',
    storageBucket: '<your-storage-bucket>',
    messagingSenderId: '<your-messaging-sender-id>'
  }
};

configure the environment

firebase


import { AngularFireModule } from '@angular/fire';
import { AngularFirestoreModule } from '@angular/fire/firestore';

...

@NgModule({
  imports: [
    BrowserModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFirestoreModule
  ],
  declarations: [ AppComponent ],
  bootstrap: [ AppComponent ]
})

import the module

import {Component} from '@angular/core';
import {AngularFireDatabase, FirebaseListObservable} from 'angularfire2/database';

@Component({
  selector: 'project-name-app',
  template: `
  <ul>
    <li *ngFor="let item of items | async">
      {{ item.name }}
    </li>
  </ul>
  `
})
export class MyApp {
  items$;

  constructor(private db: AngularFirestore) {
    this.items$ = this.db.collection('messages').valueChanges();
  }
}

firebase

import {Component} from '@angular/core';
import { AngularFirestore } from 'angularfire2/firestore';

@Component({
  selector: 'project-name-app',
  template: `
  <ul>
    <li *ngFor="let item of items | async">
      {{ item.name }}
    </li>
  </ul>
  `
})
export class MyApp {
 fbMessages: any;
 
 constructor(private db: AngularFirestore) {
    this.fbMessages = this.db.collection('messages');
    this.fbMessages.add(message);
  }
}

firebase

FORMS

FORMS

 <label for="name">Name</label>
 <input type="text" id="name" required [(ngModel)]="model.name" name="name">

 Angular forms module allow us to handle  and attach easily the model of our inputs

FORMS

<form #heroForm="ngForm">
     <div>
       <label for="name">Name</label>
       <input type="text" id="name" required
   		[(ngModel)]=“myModel.name" 
   		name="name">
	     <button type="submit">Submit</button>

</form>

ngModel directive,  used in order to synchronize our model with the data inputted. Under the hood, Angular is making a two-way data binding, using the “Banana-in-a-box” notation [()]

 

The name attribute is always required, not just because of a11y. Although, Angular uses it in the model binding

FORMS

<form>
  <input [(ngModel)]="form.name" name="name">
  <input [(ngModel)]="form.mail" name="mail">
</form>

We can handle an object with all the form data

FORMS

<form #loginForm="ngForm">
  <input name="name" required [(ngModel)]="form.name">
  <input name="password" type="password" required [(ngModel)]="form.password">
  <button type="submit"></button>
</form>
<p *ngIf="!loginForm.valid"> Fill all the fields</p>

We can create references to the full form in order to validate it fully

#loginForm = "ngForm"

FORMS

import {Component, ViewChild} from 'angular2/core';
...
@ViewChild('loginForm') public form: NgForm;
...
public showFrm(): void{
    console.log(this.form);
}

We can access to an Angular reference from the controller with 

@ViewChild('frm')

FORMS

<form #loginForm="ngForm" (ngSubmit)="login(loginForm.value)">
  <input name="name" required [(ngModel)]="form.name" >
  <input name="password" type="password" required [(ngModel)]="form.password">
  <button type="submit"></button>
</form>
<p *ngIf="!loginForm.valid"> Fill all the fields</p>

An use (ngSubmit) to handle the form submission

FORMS

<form #heroForm="ngForm">
     <div>
       <label for="name">Name</label>
       <input type="text" id="name" required
   		[(ngModel)]=“myModel.name" 
   		name="name">
	     <button type="submit">Submit</button>

</form>

Internally Angular creates FormControls and registers them with an NgForm directive that Angular attached to the

tag. Each FormControl is registered under the name we assigned to the name attribute

FORMS

When using ngModel on an input, Angular modifies automatically the className attribute of the element, adding the followings based on the element`s state

FORMS

We can use features in order to show error messages 

<input type="text" id="name"
       required
       [(ngModel)]="model.name"
       name="name"
       #name="ngModel" >
        
<div [hidden]="name.valid || name.pristine" 
     class="alert alert-danger"> Name is required </div>

FORMS

And we can add a ngSubmit to the form, using a custom action defined in our class

<form (ngSubmit)="onSubmit()" #heroForm="ngForm">

Now, the form is associated to the heroForm var, allowing us to reset it (heroForm.reset()), validate(heroForm.valid), or accessing (heroForm)

reactive FORMS

With reactive forms, you create a tree of Angular form control objects in the component class and bind them to native form control elements in the component template

 

 Create and manipulate form control objects directly in the component class. As the component class has immediate access to both the data model and the form control structure, you can push data model values into the form controls and pull user-changed values back out

reactive FORMS

IMPORTANT!

 

We have to import the

ReactiveFormsModule

reactive FORMS

FormControl tracks the value and validity status of an individual form control. It corresponds to an HTML form control such as an input box or selector.

 

FormGroup tracks the value and validity state of a group. The group's properties include its child controls. The top-level form in your component is a FormGroup.

reactive FORMS

Imagine that we want to validate a new User

export interface User {
    name: string; // required with minimum 5 chracters
    address?: {
        street?: string; // required
        postcode?: string;
    }
}

reactive FORMS

We can use FormGroup and FormControl to create validators and bind to the forms

this.myForm = new FormGroup({
    name: new FormControl('', [<any>Validators.required, <any>Validators.minLength(5)]),
    address: new FormGroup({
        street: new FormControl('', <any>Validators.required),
        postcode: new FormControl('8000')
    })
});

reactive FORMS

Or use FormBuilder to the "short" way

constructor(private formBuilder: FormBuilder) {


    this.myForm = this.formBuilder.group({
        name: ['', [<any>Validators.required, <any>Validators.minLength(5)]],
        address: this.formBuilder.group({
            street: ['', <any>Validators.required],
            postcode: ['']
        })
    });

}

reactive FORMS

And we can link class and view

<form [formGroup]="myForm">
    <div>
        <label>Name</label>
        <input type="text" formControlName="name">
    </div>
    <div formGroupName="address">
        <label>Address</label>
        <input type="text" formControlName="street">
        <small [hidden]="myForm.get('address.street').valid">
            street not valid
        </small>
    </div>
    <div formGroupName="address">
        <label>Postcode</label>
        <input type="text" formControlName="postcode">
    </div>
</form>

reactive FORMS

We can use setValue to set one input, or the whole form!

const people = {
            name: 'Jane',
            address: {
                street: 'High street',
                postcode: '94043'
            }
    };

this.myForm.setValue(people, { onlySelf: true });

reactive FORMS

And we can subscribe to the form too!

// subscribe to the stream 
this.myForm.valueChanges.subscribe(x => this.events
    .push({ event: ‘STATUS CHANGED’, object: x }));

...

this.myForm.get('name').valueChanges.subscribe((data) => {
  console.log(data);
});

reactive FORMS

Or create custom validators

import { FormControl } from '@angular/forms';

function validateEmail(c: FormControl) {
  let EMAIL_REGEXP = ...

  return EMAIL_REGEXP.test(c.value) ? null : {
    validateEmail: {
      valid: false
    }
  };
}

https://angular-maps.com/

animations

animations

Angular's animation system lets you build animations that run with the same kind of native performance found in pure CSS animations

Angular animations are built on top of the standard Web Animations API and run natively on browsers that support it

For other browsers, a polyfill is required

animations

Angular's animation system lets you build animations that run with the same kind of native performance found in pure CSS animations

Angular animations are built on top of the standard Web Animations API and run natively on browsers that support it

For other browsers, a polyfill is required

animations

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

Lets start importing the animations module from @angular/animations

animations

 animations: [
    trigger('heroState', [
      state('inactive', style({
        backgroundColor: '#eee',
        transform: 'scale(1)'
      })),
      state('active',   style({
        backgroundColor: '#cfd8dc',
        transform: 'scale(1.1)'
      })),
      transition('inactive => active', animate('100ms ease-in')),
      transition('active => inactive', animate('100ms ease-out'))
    ])
  ]

The angular animations module allow us to define transitions between states. The states are defined with the value of a binded var

animations

// "void" is a reserved name for a state and is used to represent
// the state in which an element is detached from from the application.
state("void", style({ height: 0 }))

// user-defined states
state("closed", style({ height: 0 }))
state("open, visible", style({ height: "*" }))

We can define multiple states

animations

And now, we can use it in the template to bind the animation to a concrete element

  <ul>
    <li *ngFor="let hero of heroes"
        [@heroState]="hero.state"
        (click)="hero.toggleState()">
      {{hero.name}}
    </li>
  </ul>

The hero.state will define the value of the animation state

animations


  transition('inactive => active', animate('100ms ease-in')),
  transition('active => inactive', animate('100ms ease-out')),
  transition('* => inactive', animate('100ms ease-out')),
  transition('* => *', animate('100ms ease-out')),
  transition('* => void', animate('100ms ease-out')),
  transition('void => in', animate('100ms ease-out')),



We can define multiple type of transitions, with diferent animations

animations

animations: [
  trigger('shrinkOut', [
    state('in', style({height: '*'})),
    transition('* => void', [
      style({height: '*'}),
      animate(250, style({height: 0}))
    ])
  ])
]

If we don't know the value of a dimensional style property, you can use a special * property value so that the value of the property is computed at runtime and then plugged into the animation.

animations

animations: [
  trigger('flyInOut', [
    state('in', style({width: 120, transform: 'translateX(0)', opacity: 1})),
    transition('void => *', [
      style({width: 10, transform: 'translateX(50px)', opacity: 0}),
      group([
        animate('0.3s 0.1s ease', style({
          transform: 'translateX(0)',
          width: 120
        })),
        animate('0.3s ease', style({
          opacity: 1
        }))
      ])
    ])

We can make parallel executed animations using group

animations

  <ul>
    <li *ngFor="let hero of heroes"
        (@flyInOut.start)="animationStarted($event)"
        (@flyInOut.done)="animationDone($event)"
        [@flyInOut]="'in'">
      {{hero.name}}
    </li>
  </ul>

One of the greates advantages of angular animations is that we can use callbacks in order to "do things" when the animation is complete

universal

universal

Normally, a SPA web page spend a lot of time interpreting and taking control of the view, and it takes a lot of time to see useful content on a web

rail

Response    Input latency (from tap to paint) < 100ms.

 

rail

Animation    Each frame's work (from JS to paint) completes < 16ms.

 

rail

Idle    Main thread JS work chunked no larger than 50ms.

 

rail

Load: deliver content under 1000ms

 

Old devices can browse our site because the application returns HTML, which differs from common Single Page Applications (SPA), where the tag contains JavaScript.


The first page request is fast and subsequent ones are even faster; as opposed to common SPAs, where the first request is used to load the application and then a round trip is made to fetch what was requested.

 

universal

 

 

 

Better Perceived Performance

 

Optimized for Search Engines

universal

The Angular Universal project consists of the base platform API and the surrounding tools that enables developer to do server side rendering(or pre-rendering) of Angular application

 

CONSTRUCTION & DEPLOYMENT

Webpack is a module bundler, a simple tool that takes just the needed resources of our app, transforms them based on our configuration files, and put them together in order to minimize the application modules and files

cONSTRUCTION

We can install webpack trough npm

cONSTRUCTION

npm install -D webpack ts-loader html-webpack-plugin tslint-loader

This way, we are installing some importante dependencies for the Angular development, such as the ts compiler for webpack, an html loader in order to minimize an “inlining” the html, and validate our code style with the tslint-loader

cONSTRUCTION

We can split our app in “bundles”, which means a composition of modules. For example, we can have a bundle for the external dependencies, another one for our main module, and one more by each lazy load module.

cONSTRUCTION

In webpack, we define the output bundles, and how are they constructed with the “entry” object 

{
  entry: {
    app: './src/app/main.ts',
    vendor: [
        'es6-shim',
        'angular2/bundles/angular2-polyfills',
        'angular2/bootstrap',
        'angular2/platform/browser',
        'angular2/platform/common_dom',
        '@angular/core',
        'angular2/router',
        'angular2/http',
      ]
    }
}

cONSTRUCTION

And now, we can define how our bundles gonna be transformed, with loaders or plugins. For example, we are gonna use a loader, in order to compile typescript to javascript

{
  test: /\.ts$/,
  loader: 'ts-loader',
  exclude: /node_modules/
}

cONSTRUCTION

We can use plugins too, in order to add some actions to the web pack bundler process, such as insert automatically the output bundlers in our app

new HtmlWebpackPlugin({
  template: './src/index.html',
  inject: 'body',
  minify: false
});

cONSTRUCTION

Lets check of looks a webpack file

Compilation

Angular applications are built with templates, which may be .html or .css files, or may be inline template attributes on Decorators like @Component.

 

These templates are compiled into executable JS at application runtime.

Compilation

Compile all of this resources in runtime requires the angular compiler as part of the final bundle, and can be slow to compile huge amount of data, templating and necessary files.

 

This is known as JiT (Just In Time)

Compilation - JIT

JiT is the simpler compilation method. We can use the angular cli to do it with

ng build --prod

Compilation - JIT

If we look closer at the entry point of our app, we will se something like this

import { ViewEncapsulation } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app';

platformBrowserDynamic()
  .bootstrapModule(AppModule);

We can see that we are importing AppModule directing from app, without compiling. Webpack will transform it with loaders and plugins

Compilation - aot

In order to minimize compilation time on runtime, and improve performance, Angular team has created a runtime compilation in order to avoid this process on the browser.

 

This compilation reduces the bundles size in 60%, and improves a lot the performance in the pages load

This is known as AoT (Ahead of time)

Compilation - aot

AoT is the "before browser" compilation method. We can use the angular cli to do it with

ng build --aot --prod

Compilation - aot

AoT is the "before browser" compilation method. We can use the angular cli to do it with

ng build --aot --prod

Compilation - aot

In order to use this kind of compilation without the cli, we should have something like this entrypoint

/*
 * Angular bootstraping
 */
import { AppModuleNgFactory } from '../compiled/src/app/app.module.ngfactory';
import { platformBrowser } from '@angular/platform-browser';

platformBrowser()
  .bootstrapModuleFactory(AppModuleNgFactory);

We can see that the import of AppModule comes from a "compiled" dir. This is the entry point of the AoT compiled modules.

Compilation - aot

In order to add this compilation step in our typescript config file, we can add a new "angularCompilerOptions" option


  "angularCompilerOptions": {
   "genDir": "aot",
   "skipMetadataEmit" : true
 }

This way we can tell our application to create compiled aot modules

Testing

UNIT TESTING

Unit testing refers to the practice of testing certain functions and areas – or units – of our code. This gives us the ability to verify that our functions work as expected. That is to say that for any function and given a set of inputs, we can determine if the function is returning the proper values and will gracefully handle failures during the course of execution should invalid input be provided.

UNIT TESTING

  • Helps identifying failures in our logic
  • Write code "easy to test"
  • Prevent future "build breaks"
  • Simplifies integration & reusability
  • Can work as functionality doc validation

UNIT TESTING

JavaScript test runner

Testing framework

UNIT TESTING - jasmine

suite: collection of test cases that are intended to be used to test a software program to show that it has some specified set of behaviours

spec: test case for a concrete specification of the component / file to test

UNIT TESTING - jasmine

describe("AppComponent", function() {
  var a;

  it("a should be true", function() {
    a = true;

    expect(a).toBe(true);
  });
});

This is a basic test with Jasmine

UNIT TESTING - ANGULAR

Angular recomends this stack in order to test our componentss, and provide some tools to help us configuring our dependencies on our tests

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

import { By }              from '@angular/platform-browser';

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

UNIT TESTING - ANGULAR

TestBed

 It creates an Angular testing module—an @NgModule class—that you configure with the configureTestingModule method to produce the module environment for the class you want to test.

In effect, you detach the tested component from its own application module and re-attach it to a dynamically-constructed Angular test module tailored specifically for this battery of tests.

UNIT TESTING - ANGULAR

TestBed

TestBed.configureTestingModule({
  declarations: [ BannerComponent ], // declare the test component
});

UNIT TESTING - ANGULAR

createComponent

After configuring TestBed, you tell it to create an instance of the component-under-test. We will use TestBed.createComponent to create an instance of a Component and returns a component test fixture.

The createComponent method closes the current TestBed instance to further configuration

UNIT TESTING - ANGULAR

TestBed

fixture = TestBed.createComponent(BannerComponent);
comp = fixture.componentInstance; // BannerComponent test instance

The Fixture, is a handle on the test environment surrounding the created component. It provides access to the component instance itself and to the DebugElement, which is a handle on the component's DOM element.

UNIT TESTING - ANGULAR

Basic test with angular

  it('should increment number', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const elm = fixture.componentInstance;
    expect(elm.value).toBe(5);
    elm.testFuncion();
    expect(elm.value).toBe(6);
  }));

The automatic change detection is disabled in angular testing by default, in order to give the developer more control over the asynchronous and no controlled changes

UNIT TESTING - ANGULAR

Basic test with angular

import { ComponentFixtureAutoDetect } from '@angular/core/testing';

TestBed.configureTestingModule({
  declarations: [ BannerComponent ],
  providers: [
    { provide: ComponentFixtureAutoDetect, useValue: true }
  ]
})

We can activate automatic detection importing ComponentFixtureAutoDetect and adding it to providers

UNIT TESTING - ANGULAR

The automatic change detection is disabled in angular testing by default, in order to give the developer more control over the asynchronous and no controlled changes

it('should still see original title after comp.title change', () => {
  const oldTitle = comp.title;
  comp.title = 'Test Title';
  // Displayed title is old because Angular didn't hear the change :(
  expect(el.textContent).toContain(oldTitle);
});

it('should display updated title after detectChanges', () => {
  comp.title = 'Test Title';
  fixture.detectChanges(); // detect changes explicitly
  expect(el.textContent).toContain(comp.title);
});

UNIT TESTING - ANGULAR

If we need to inject a service, we should use a copy, in order to no create or depends on a external element

const userServiceStub = {
  isLoggedIn: true,
  user: { name: 'Test User'}
};

TestBed.configureTestingModule({
   declarations: [ WelcomeComponent ],
   providers:    [ {provide: UserService, useValue: userServiceStub } ]
});

userService = TestBed.get(UserService);

UNIT TESTING - ANGULAR

If we want to test an event, an async service or a service petition, we can use spies.

A spy can stub any function and tracks calls to it and all arguments. There are special matchers for interacting with spies. The toHaveBeenCalled matcher will return true if the spy was called

UNIT TESTING - ANGULAR

We can use a different value for a service in order to test it

let mockRouter = {
    navigate: jasmine.createSpy('navigate')
};

beforeEach(() => {
  TestBed.configureTestingModule({
  declarations: [ NavToolComponent ],
  providers: [
    { provide: Router, useValue: mockRouter }
  ]})
})

...

expect(mockRouter).toHaveBeenCallWith(['/home']);

UNIT TESTING - ANGULAR

spyOn(callToService, 'getUsers').andReturn(fakeHttpPromise);

component.updateUser();

expect(callToService.getUsers).toHaveBeenCalled();

component.updateUser('admin');

expect(callToService.getUsers).toHaveBeenCalledWith('admin');

UNIT TESTING - ANGULAR


  it('should emit on filter click', inject([], () => {
    let valueAfterClick;

    productListComp.itemClicked.subscribe(item => { 
        console.log(item);
        valueAfterClick = item;
    });

    let el = fixture.debugElement.children[0];
    let card = el.query(By.css('.global-position__personal')).nativeElement;

    card.click();
    expect(valueAfterClick).toBe('profile');
  }));

Testing events

UNIT TESTING - ANGULAR

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

import {TeoLoginComponent} from './teo-login.component';

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

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

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

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

testing providers - ANGULAR

let httpClientSpy: { get: jasmine.Spy };
let heroService: HeroService;
 
beforeEach(() => {
  // TODO: spy on other methods too
  httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
  heroService = new HeroService(<any> httpClientSpy);
});
 
it('should return expected heroes (HttpClient called once)', () => {
  const expectedHeroes: Hero[] =
    [{ id: 1, name: 'A' }, { id: 2, name: 'B' }];
 
  httpClientSpy.get.and.returnValue(asyncData(expectedHeroes));
 
  heroService.getHeroes().subscribe(
    heroes => expect(heroes).toEqual(expectedHeroes, 'expected heroes')
  );
})

UNIT TESTING - ANGULAR

We can test our components with the cli

$ ng test

or test generating coverage report

$ ng test --code-coverage

Angular

By Jon Rojí

Angular

Angular 2 starting slides

  • 2,773