Angular 2

Components

Basic Component

  • Basic component is composed of:
    • Component metadata
    • Component controller class (behaviour)
    • Attaching metadata to a Component can be done using TypeScript decorator @Component
      • TypeScript decorators are simply functions that modify a class, property, method or method parameters. Syntax is an “@” symbol followed by a function.
      • Deocrators functions should be imported as needed
import {Component} from 'angular2/core';

@Component({ 
    ...
})
class Component {
    constructor() {}
}

Component Metadata

  • Basic @Component metadata include:
    • selector ([string]) - CSS selector syntax for identifying the component element in the template
    • template (string) - The Component's view. Template include DOM elements and bindings (for elements inputs and outputs)
      • Optionally use templateUrl
import {Component} from 'angular2/core';

@Component({
    selector: 'my-first-component',
    template: `<h1>This is my first component</h1>`
})
class MyComponent {
    constructor() {}
}

Bootstrap

  • Each application need to have a Root Component
  • Root component is the top most component used for bootstrapping an Angular 2 application
  • The Bootstrap requires the following:
    • Platform sepcific bootstrap module (default for browsers)
    • Root component
    • Optionally, list of providers for Dependency Injection
  • Bootstrap returns a Promise with the Application (Component) reference (or error)
  • The Root Component element (as described in the Component's selector) should be included in the entry index.html

Bootstrap

import {bootstrap} from 'angular2/platform/browser';

import {MyComponent} from './my-component.component';

bootstrap(MyComponent)  // Additional parameter is an array of providers
    .catch( err => console.log(err));

One small thing is missing. Can you guess?

Bootstrap

<!DOCTYPE html>
<html>
<head>
    <script src="src/my-angular-bootstrap.js"></script>
</head>
<body>
    <my-first-component></my-first-component>
</body>
</html>
  • Let's look at index.html
  • Webpack (or other modules/bundler) can be used to pack all necessary assests for Angular 2 application

Template Syntax

  • The Component's template can include the following:
    • HTML elements
    • CSS styles (will be in Shadow DOM if enabled)
    • Expressions / Statements
    • Bindings
      • Properties
      • Events
    • Interpolation

Template Syntax

  • Expressions
    • Template expression
      • Can include calculations/functions
      • Can include strings/texts
      • Can include Component's members
      • Can include invoked Component's methods
      • Evaluate according to target 
    • Template statements
      • Include Component's invoked method
    • Examples:
      • "1+1", "getData()", "'text'", etc.

Template Syntax

  • Bindings
    • Prorpery binding - binding a DOM Element's propery to a template expression
      • Syntax: [<property>] = "<template expression>"
      • Direction: Component -> DOM Element
      • It is important to differentiate between HTML attributes and DOM Element properties
      • In angular 2, data-binding only relates to properties and not attributes
      • Examples:
<img [src]="..." />
<img src="..." /> <!-- similar to: <img [src]="'...'" />
<div [disbaled]="'true'"></div>

Template Syntax

  • Bindings
    • Event binding - binding a DOM Element's event to template statement expression
      • Syntax: (<event>)="<template statement expression>"
      • Direction: DOM Element -> Component
      • Information about the event and data values are accessible through an event object named $event.
      • Angular 2 Components can emit custom events using EventEmitter, passing payload through the $event object
      • Examples:
<button (click)="onSave()">On Save</button> <!-- "on-click" is an alternate syntax -->
<input [value]="currentHero.firstName"
       (input)="currentHero.firstName=$event.target.value" >
<my-component (myEvent)="doSomething($event)"></my-component>

Template Syntax

  • Bindings
    • Attribute bindings - binding a DOM Element's attribute to template statement expression
      • Syntax: (attr<.attribute>)="<template statement expression>"
      • Direction: Component -> DOM Element
      • Examples:
<button [attr.aria-label]="actionName">{{actionName}} with Aria</button>
<table border=1>
  <!--  expression calculates colspan=2 -->
  <tr><td [attr.colspan]="1 + 1">One-Two</td></tr>

  <!-- ERROR: There is no `colspan` property to set!
    <tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
  -->

  <tr><td>Five</td><td>Six</td></tr>
</table>

Template Syntax

  • Bindings
    • Class bindings - binding a DOM Element's class to template statement expression
      • Syntax: (class<.classname>)="<template statement expression>"
      • Direction: Component -> DOM Element
      • Examples:
<!-- standard class attribute setting  -->
<div class="bad curly special">Bad curly special</div>

<!-- reset/override all class names with a binding  -->
<div class="bad curly special"
     [class]="badCurly">Bad curly</div>

<!-- toggle the "special" class on/off with a property -->
<div [class.special]="isSpecial">The class binding is special</div>

<!-- binding to `class.special` trumps the class attribute -->
<div class="special"
     [class.special]="!isSpecial">This one is not so special</div>

Template Syntax

  • Bindings
    • Style bindings - binding a DOM Element's style to template statement expression
      • Syntax: (style<.stylename>)="<template statement expression>"
      • Direction: Component -> DOM Element
      • Examples:
<button [style.color] = "isSpecial ? 'red' : 'green'">Red</button>
<button [style.backgroundColor]="canSave ?'cyan' : 'grey'" >Save</button>

<button [style.fontSize.em]="isSpecial ? 3 : 1" >Big</button>
<button [style.fontSize.%]="!isSpecial ? 150 : 50" >Small</button>

Template Syntax

  • Bindings
    • Component/Directive Custom bindings - binding for a component or directive.
    • Work same as DOM Elements bindings
    • Custom bindings for component are:
      • Property bindings for Component inputs
      • Event bindings for Component outputs
    • Examples:
<hero-detail [hero]="currentHero"></hero-detail>
<hero-detail (deleteRequest)="deleteHero()"></hero-detail>
<div (myClick)="clicked=$event">click me</div>

Template Syntax

  • The Component's template can include the following:
    • Interpolation:
      • Double curly brackets syntax {{<template expression>}}
      • Expression can include calculations, Component's memebers, Component's invoked method, etc.
      • The expression always results into string/text
      • Examples: {{1+1}}  - '2', {{name} , {{getName()}}
      • Interpolation is short syntax for property binding:
        [textContent]="interpolate(...)
<p>Hello {{name}}</p>
<p [textContent]="interpolate(['Hello'], [name])"></p> <!-- same as above -->

Template Syntax

  • Bindings Summary
Binding Type Target Example
Interpoluation Element property <div>{{name}}</div>
Property Element property
Component property
Directive property
<img [src] = "heroImageUrl">
<hero-detail [hero]="currentHero"></hero-detail>
<div [ngClass] = "{selected: isSelected}"></div>
Event Element event
Component event
Directive event
<button (click) = "onSave()">Save</button>
<hero-detail (deleteRequest)="deleteHero()"></hero-detail> <div (myClick)="clicked=$event">click me</div>
Attribute Attribute (the exception) <div [class.special]="isSpecial">Special</div>
Class class property <div [class.special]="isSpecial">Special</div>
Style style property <button [style.color] = "isSpecial ? 'red' : 'green'">
template: `
<div>
  <img src="{{heroImageUrl}}">
  <span [style.text-decoration]="lineThrough">
    {{prefix}} {{hero?.fullName}}
  </span>
  <button (click)="delete()">Delete</button>
</div>`

Template Local Variables

  • Local variables is a way to exchange data between DOM Elements in the template
  • Angular 2 allows us to define variables for elements as follows: #<variable name> (can also use: var-)
  • The value assigned to the variable depends on the context:
    • When there is no Directive the value is set the the DOM Element
    • When Directive is present the Directives sets the value (e.g NgFor). The Directive can expose (export) specific values that can be used by local varibales.
      In this case the syntax will be
      #<variable name>="<directive export>"

Template Local Variables

  • Examples
<!-- phone refers to the input element; pass its `value` to an event handler -->
<input #phone placeholder="phone number">
<button (click)="callPhone(phone.value)">Call</button>

<!-- fax refers to the input element; pass its `value` to an event handler -->
<input var-fax placeholder="fax number">
<button (click)="callFax(fax.value)">Fax</button>

<form (ngSubmit)="onSubmit(theForm)" #theForm="ngForm">
  <button type="submit" [disabled]="!theForm.form.valid">Submit</button>
</form>

<template ngFor #hero [ngForOf]="heroes" [ngForTrackBy]="trackByHeroes">
  <hero-detail [hero]="hero"></hero-detail>
</template>

Template Element Content

  • DOM Elements can have content.
  • The element content is anything between the element tags: 
    <element>Content</element>
  • Something this is refered to as LightDOM vs. ShadowDOM which is information encapsulated inside the element
  • Component templates can display this content using the 
    <ng-content> tag
  • This is similar to directive transclude in Angular 1.x
// Defined in Component
selector: 'my-component',
template: `<div>I am component</div>
           <ng-content></ng-content>`


// Used in parent component template:
template: `<my-component>This is my Content</my-component>

Inputs

  • Component inputs defined as custom property bindings that will be used as inputs for the Component
  • Inputs are binding to class members
  • Component inputs can be defined in two ways:
    • @Component decorator
      • inputs: ['<internal name>:<external name>']
    • Using @Input()
      • @Input('<external name>') <internal name> : <type>
    • External name is the name used when using the property binding in the template
    • Internal name is not mandatory. Name can be the same internally and externally

Inputs

  • Examples:
import {Component, Input} from 'angular2/core';

@Component({
    selector: 'my-component',
    template: `<h1>My Component</h1>`,
    inputs: ['myOtherColor:othercolor']
})
class MyComponent() {
    
    @Input('color') myColor : string;
    constructor() {
        console.log(this.myColor + ' ' + this.myOtherColor);
    }
}
template: `<div>
               <my-component [color]="'lightblue'" [othercolor]="'lightyellow'">
               </my-component>
           </div>`
  • Usage in parent Component's template:

Outputs

  • Component outputs defined as custom event bindings that will be used as outputs for the Component
  • Events are implemented using EventEmitter module that implements RxJS Observable
  • Component outputs can be defined in two ways:
    • @Component decorator
      • outputs: ['<internal name>:<external name>']
    • Using @Output()
      • @Output('<external name>') <internal name> = new EventEmitter();

Outputs

  • EventEmitter API
    • From Component send event: 
      • EventEmitter.emit(<payload>) 
    • Parent Component's template bind to event:
      `<component 
           (<event-name>)="<eventHanlderFn>($event)>
      </component>`
    • Event is handled in parent component implementing an event hanlder method in the parent Component class
    • Event information is passed by $event object

Outputs

  • Example
import {Component} from 'angular2/core';

@Component({
  selector: 'counter',
  template: `// omitted for brevity`,
  outputs: ['counterChange:change']
})
export class CounterComponent {
  public counterValue = 0;
  increment() {
    this.counterValue++;
    this.counterChange.emit({value: this.counterValue})
  }
  decrement() {
    this.counterValue--;
    this.counterChange.emit({value: this.counterValue})
  }
}
<counter (change)="myValueChange($event);"></counter>
  • Usage in parent Component template:

Builtin Directives

  • Directives are Components that have no view
  • Directives are used to attach behaviour to DOM elements
  • Since Directives are base Components, they can also use property binding for inputs and event binding for outputs
  • There are two main Directive categories:
    • Attribute
      • Change the appearance or behavior of a DOM element (e.g. NgClass)
    • Structural
      • Change the DOM layout by adding or removing elements (e.g. NgIf)

Builtin Directives

  • Angular 2 comes with builtin directives that are available by default to all Components
    • Attribute: NgStyle, NgClass
    • Structural: NgIf, NgSwitch, NgFor
  • Structural Directives actually use a <template> element as the base element that will be used to change the DOM layout. 
    • Instead of using <template> element, there is a short syntax adding  '*' before the directive: e.g. : *ngIf. This way the <template> element is created for us
    • Structural Directives also use local variables for operations that need a variable like NgFor

Builtin Directives

  • It should be noted that Angular 2 comes with less builtin Directives than Angular 1. This is because Angular 2 can use property/event bindings to achieve the same functionality without need for specific Directive
    • e.g. ng-click (in 1.x) - event binding for (click)
    • e.g. ng-show (in 1.x) - property biding to [hidden]
  • Angular 2 also allows creating custom Directives as needed

Builtin Directives

  • Usage examples
<!-- NgClass -->
<div [ngClass]="{active: isActive}">
<div [ngClass]="{active: isActive, shazam: isImportant}">
<div [class.active]="isActive">

<!-- NgStyle -->
<div [ngStyle]="{color: colorPreference}">
<div [style.color]="colorPreference">

<!-- NgIf -->
<table *ngIf="movies.length">

<!-- NgFor -->
<tr *ngFor="#movie of movies">
<template ngFor #hero [ngForOf]="heroes" [ngForTrackBy]="trackByHeroes">
  <hero-detail [hero]="hero"></hero-detail>
</template>

<!-- NgSwitch -->
<span [ngSwitch]="favoriteHero && checkMovieHero(favoriteHero)">
  <p *ngSwitchWhen="true">Excellent choice!</p>
  <p *ngSwitchDefault>Please enter your favorite hero</p>
</span>

Builtin Directives - *NgFor

  • Structural Directives use <template> element to change the DOM layout
  • For example we can look at NgFor
  • The syntax can be in different formats that are all the same:
    • <li *ngFor="#item of items; #i = index">...</li>
    • <li template="ngFor #item of items; #i = index">...</li>
    • <template ngFor #item [ngForOf]="items" #i="index"><li>...</li></template>
  • The last example shows the <template> in a clear way where the duplicated area is the context inside the <template> element.
  • Local variables are used in the context of the Directives and not the element (#<variable>='...')

Builtin Directives

  • There are several values that are exposed by NgFor and can be used with local variables:
    • index - will be set to the current loop iteration for each template context.
    • last - will be set to a boolean value indicating whether the item is the last one in the iteration.
    • even - will be set to a boolean value indicating whether this item has an even index.
    • odd - will be set to a boolean value indicating whether this item has an odd index.

Builtin Pipes

  • Pipes in Angular 2, are enquivalent to Angular 1.x filters
  • Pipes transform displayed values within a template.
  • Pipes are mainly used in template inside interpolation {{}} to transform into display-friendly format
  • Syntex: using pipe-operator (|) on the transformed value
  • Pipes can get parameters, using semicolor (:) syntax
    • e.g.  date:"MM/dd/yy"
  • Pipes can be chained
    • e.g. {{ birthday | date:'fullDate' | uppercase}}
  • ​There are builtin pipes available by default to all components
  • Angular 2 allows creating custom Pipes as needed

Builtin Pipes

Component Tree

  • Angular 2 application is a component tree
  • Components can have child components strating from the Root Component
  • Each Component can define the Components it can use that will be its Child Componetns
  • Components hierarchy is a graph of directed tree. Angular 2 does not allow breaking it (throws).
  • Component defines it's dependant Child Components in the @Compoent decorator directives list
  • Child components are imported (and exported) as modules
  • Child components can be used in the parent tempalte as any other DOM element. Inputs and Ouputs can be used for parent child communication

Component Tree

  • Example:
// Parent Component
import {Component} from 'angular2/core';
import {ChildComponent} from './my-child.component';

@Component({
    selector: 'my-component',
    template: `<child-component [param]="param" (childevent)="onChildEvent($event)">
               This is child Content
               </child-component>`,   
    directives: [ChildComponent]
})
class MyComponent() {
    onChildEvent(data) { consloe.log(data); }
}
// Child Component
import {Component} from 'angular2/core';

@Component({
    selector: 'child-component',
    template: `<div>I am a child</div><ng-content></ng-content>`,
})
export class ChildComponent() {
    @Input() param : any;
    @Output() childevent : EventEmitter<any> = new EventEmitter();
}

Component Life Cycle

  • Directive and component instances have a lifecycle as Angular creates, updates, and destroys them.
  • Angular offers component lifecycle hooks that give us visibility into these key moments and the ability to act when they occur.
  • Components are extended Directives so they have extended life cycle hooks
  • It is advisable to do initialization in Component OnInit hook and not constructor (some object are still not initialized)

  • Other Angular sub-system may have their own lifecycle hooks apart from the component hooks. The router, for instance, also has its own router lifecycle hooks that allow us to tap into specific moments in route navigation.

Component Life Cycle

  • Component / Directive life cycle hooks
Hook Timing Component
Directive
ngOnChanges before ngOnInit and when a data-bound input property value changes C, D
ngOnInit after the first ngOnChanges C, D
ngDoCheck during every Angular change detection cycle C, D
ngAfterContentInit after projecting content into the component C
ngAfterContentChecked after every check of projected component content C
ngAfterViewInit after initializing the component's views and child views C
ngAfterViewChecked after every check of the component's views and child views C
ngOnDestroy just before Angular destroys the directive/component C, D

Component Life Cycle

  • Life cycle hooks can be used programmatically as needed
  • Relevant hook needs to be imported and implemeted as part of the Component / Directive class
import {Component, OnInit} from 'angular2/core';

@Component({
})
class MyComponent implements OnInit {
    constructor() {}

    ngOnInit() {
        console.log('Component init');
    }
}

Angualr 2 Components

By Shaul Ben-Yossef

Angualr 2 Components

  • 1,525