intro to ng2

Angular2 aka angular.io aka ng2 aka Angular

(as opposed to AngularJS which refers to Angular1)

Ben Kinsey @bkinsey808

Github Demo Project: github.com/bkinsey808/bk-ng2-ultra

What is Angular2?

A framework for building mobile and desktop applications
A compiler!
(sort of)
ng2 vs react
typescript written in es5
beta.2 version 0.15
google megacorp facebook
framework considered as library
more opinionated less opinionated
more corporate more hipster
ng2 vs react
dirty checking change detection virtual dom diffing
more complex simpler
more extensible change detection strategy less extensible

ng2 change detection

super optimized

different strategies for different scenarios

jit change detection

inline caching

only updates changed bindings

can be further optimized with immutables and observables

ng2 uses Zone.js

tells ng2 when to run change detection

How it works:

intercepts all asynchronous events so they map to the same thread local context

MAGIC!

  • no more digest loop
  • no more $timeout
  • no more $scope.$apply(), ever

ng2 is engineered to be Opinionated

Even more opinionated than ng1, and ng1 was considered a relatively opinionated framework

Why is Opionated a Good Thing?

Angular community includes 1.1 million active developers

Massive scale community consensus and synergy

Learn TypeScript!

(Don't worry, it's awesome)

It's just...
ES6 + ES* + optional static typing
That's it!!!
AngularJS
import angular, {bootstrap}
  from 'angular';

angular.module('app', [])
.component('app', {
  restrict: 'E',
  template: `
    <div>
      Hello World
    </div>
  `,
  controller: class App {}
});

bootstrap(document, ['app']);
Angular2
import {bootstrap} from
  'angular2/platform/browser';
import {Component} from
  'angular2/core';

@Component({
  selector: 'app',
  template: `
    <div>
      Hello World
    </div>
  `
})
class App {}

bootstrap('App', []);
Slide credit: Patrick Stapleton

What the @ are decorators?

simply functions that modify:

• methods

• method parameters

• properties

• classes

Decorators are typically used to add metadata, but could also add mixin-like functionalty

@Component - tells ng2 that the class defines a component, and also how to process it

  • selector - a css selector (DOM element tag) that tells ng2 to create and insert an instance of this component in the parent HTML
  • template - the template string

An ng2 app is a tree of components

A component is self-describing:

  • knows how to interact with its host element.
  • knows how to render itself.
  • configures dependency injection
  • has a well-defined public API of input and output properties.

Scope is not inherited down the DOM tree. There is no scope.

What's up with Dependency Injection?

"Don't call me, I'll call you"

The technical name for this is "Inversion of Control"

ng2, like ng1, controls how dependencies get injected into your custom code

This is a design pattern that increases modularity and extensibility

The evolution of Dependency Injection

ng1 uses quoted strings:
  ['a', 'b', function(a, b) {..}]
This is necessary, because under the hood, a simple object in ng1 is used to store the name-value (token-object) pairs of each dependency:
  di = {'a': a, 'b': b}
In ng2, dependency injection is implemented as an es6 Map(), which can take objects, functions, etc as keys, not just simple strings.
 di = new Map()
The end result is that in ng2 dependencies can be modeled as a simple array:
  providers: [a, b]

What is a Provider?

an instruction that describes how an object for a certain token is created

In ng2 there are two ways to inject an object (typically a service) so that it can be used by a component:

// at bootstrap
bootstrap(AppComponent, [DataService]);

// in a component
@Component({
  ...
  providers: [DataService]
})
class AppComponent {
  constructor(dataService: DataService) {
    // dataService instanceof DataService === true
  }
}

Multi Providers: ng2 pluggable hooks

can provide multiple tokens

can also be used to extend...

class Engine { }
class TurboEngine { }

var injector = Injector.resolveAndCreate([
  provide(Engine, {useClass: Engine}),
  provide(Engine, {useClass: TurboEngine})
]);

var engine = injector.get(Engine);
// engine instanceof TurboEngine

ng2 encourages "composition over inheritance"

Last token WINS

...a very nice feature to implement a pluggable interface that can be extended from the outside world

...including ng2 platform directives

Wait... did you say Directives?

Directives are classes which get instantiated as a response to a particular DOM structure

By controlling the DOM structure, what directives are imported, and their selectors, the developer can use the "composition pattern"

using simple objects to build complex ones.

Directives are the cornerstone of an Angular application.

Directives allow the developer to turn HTML into a DSL and then control the application assembly process.

ng2 directives

Directives are instantiated whenever the CSS selector matches the DOM structure.

@Component is a special kind of @Directive that matches the tag selector

@Directive is used when matching any other css selector (e.g. custom tooltip attribute)

Let's talk Templates

ng2 templates are compiled at compile time instead of runtime for optimization

Key Point: same syntax for custom and native components

friendly for tooling (though not much ng2 template tooling has been built yet)

Explicit Template Syntax

makes template easier to refactor without understanding underlying components

for example, *ngIf and *ngFor cannot be combined in same element, therefore there is no need for the concept of "directive priority"

What the #*{{|}}*[()] did you do to my HTML?

calm down, it actually makes a lot of sense

[] property binding

() event binding

[()] two way binding

{{}} interpolation

* template tag (ngIf, ngFor, etc)

# local variable

| pipe

The evolution of forms

ng1 forms rely on the ng-model directive

instantaneous two-way data binding keeps a form control in sync with a view model.

However, the approach has some disadvantages:

  • everything is mutable
  • strange, unintended side-effects if same model is used elsewhere
  • validation logic often contaminates templates
  • form testing must be end-to-end because forms are implemented directly as DOM.
Template-driven forms
  • quick and easy, similar to ng1 forms
  • simpler, easier, and similar to ng1 forms
  • Can be implemented with very little component code
  • Hard to unit test
  • allows validation logic to infest the template (bad!)
  • does not work with immutable objects
  • everything is mutable, almost diametric opposite of redux pattern
Model-based forms
  • best practice for advanced use cases
  • more complex and very different from ng1 forms
  • requires non-trivial component code
  • validation logic must be in the component code (good!)
  • works with immutable objects
  • state is now easier to centralize and manage, making redux pattern possible
  • form and controls are observables leading to modern FRP (functional reactive programming) patterns

What's an Observable and why is it a big deal?

Function types in es7/es2016

Synchronous Asynchronous
function T Promise
function* Iterator Observable?

Ready for the Rx Revolution?

in ng1 promises were prevalent, in ng2 it will be observables.

observables have advantage over promises:

  • disposability (can be canceled before completed)
  • lazy (will not get evaluated unless subscribed)
  • re-usable (can be subscribed to and unsubscribed to multiple times)
  • returns a stream of values, like an array but async
  • can handle backpressure
  • much better suited for real world client-server communication than promises

Template-driven Form Example Template

<form #f="form"
      (ng-submit)="onSubmitTemplateBased()">
  <label>First Name:</label>
    <input type="text"
         ng-control="firstName" 
         [(ng-model)]="vm.firstName" required>
  <label>Password:</label>
  <input type="password"
         ng-control="password" 
         [(ng-model)]="vm.password" required>
  <button type="submit"
          [disabled]="!f.valid">
    Submit
  </button>
</form>

Model-based Form Example Template

<form [ng-form-model]="form"
      (ng-submit)="onSubmitModelBased()">
  <label>First Name:</label>
  <input type="text"
         ng-control="firstName">
  <label>Password:</label>
  <input type="password"
         ng-control="password">
  <button type="submit"
          [disabled]="!form.valid">
    Submit
  </button>
</form>

Template-driven Form Example Component

@Component({
  selector: "template-driven-form",
  templateUrl: 'template-driven-form.html',
  directives: [FORM_DIRECTIVES]
})
export class TemplateDrivenFormComponent {
  vm: Object = {};
  onSubmitTemplateBased() {
    console.log(this.vm);
  }
}

Template-driven Form Example Component

@Component({
  selector: "model-driven-form",
  templateUrl: 'model-driven-form.html',
  directives: [FORM_DIRECTIVES]
})
export class ModelDrivenFormComponent {
  form: ControlGroup;
  firstName: Control = new Control("", Validators.required);
  constructor(fb: FormBuilder) {
    this.form = fb.group({
      "firstName": this.firstName,
      "password": ["", Validators.required]
    });
  }
  onSubmitModelBased() {
    console.log(this.form);
  }
}

Now comes the fun part.

This is FRP (Functional Reactive Programming)

this.form.valueChanges
.map((value) => {
  value.firstName = value.firstName.toUpperCase();
  return value;
})
.filter((value) => this.form.valid)
.subscribe((value) => {
  alert("View Model = " + JSON.stringify(value));
});
ng2's http.get() returns an observable
getTasks() {
  return this.http
  .get('/api/v1/tasks.json')
  .map( (responseData) => {
    return responseData.json();
  })
  .map((tasks: Array) => {
    let result:Array = [];
    if (tasks) {
      tasks.forEach((task) => {
        result.push(
        new Task(
          task.id, 
          task.description,
          task.dueDate,
          task.complete));
        });
      }
      return result;
    });
  }
}

This is a service that wraps an observable

@Injectable()
export class WikipediaService {
 constructor(
   private jsonp: Jsonp) {}
 search (term: string) {
    var search = new URLSearchParams()
    search.set('action', 'opensearch');
    search.set('search', term);
    search.set('format', 'json');
    return this.jsonp
    .get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK', {
      search
    })
    .map((response) =>
      response.json()[1]);
  }
}

Search-as-you-type powered by observables

@Component({
  selector: 'wikipedia-search',
  providers: [WikipediaService],
  template: `
    <div>
      <h2>Wikipedia Search</h2>
        <input
          type="text"
          [ngFormControl]="term">
      <ul>
        <li *ngFor="#item of items 
                    | async">
        </li>
      </ul>
    </div>
  `
})
export class WikipediaSearchComponent {
  items: Observable<Array<string>>;
  term = new Control();
  constructor(
    private wikipediaService:
      WikipediaService) {
      this.items =
        this.term.valueChanges
        .debounceTime(400)
        .distinctUntilChanged()
        .switchMap(term =>
          this.wikipediaService
          .search(term));
  }
}

A taste of learning about Rx instance operators

With flatMap, the search results could be stale, because search responses may come back out of order. To fix this, switchMap should be used, since it ensures that an old observable is unsubscribed once a newer one is provided.

So, in summary, flatMap should be used when all results matter, regardless of their timing, and switchMap should be used when only results from the last Observable matter.

The stateful AsyncPipe

The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically, eventually returning the emitted value(s).

It is stateful because the pipe maintains a subscription to the input and its returned values depend on that subscription.

Bottom Line:

ng2 plus Rx pushes the state-of-the-art dramatically.

It's never been easier to write powerful, responsive UI.

Hopefully, this presentation has given you a little glimpse into emerging patterns that I predict will become well-used in this field in the future.