Friday, March 20, 2015

Forms in Angular 2

Input handling is an important part of application development. The ng-model directive provided in Angular 1 is a great way to manage input, but we think we can do better. The new Angular Forms module is easier to use and reason about than ng-model, it provides the same conveniences as ng-model, but does not have its drawbacks. In this article we will talk about the design goals for the module and show how to use for common use cases.

Optimizing for real world apps

Let's look at what a simple HelloWorld example would look like in Angular 2:

@Component({
  selector: 'form-example'
})
@Template({
  // we are binding the input element to the control object
  // defined in the component's class 
  inline: `<input [control]="username">Hello {{username.value}}!`,
  directives: [forms]
})
class FormExample {
  constructor() {
    this.username = new Control('World');
  }
}

This example is written using TypeScript 1.5 that supports type and metadata annotations, but it can be easily written in ES6 or ES5. You can find more information on annotations here. The example also uses the new template syntax that you can learn about by watching the keynote Misko and Rado gave at NgConf.

And, for comparison sake, here's what we'd write in Angular 1:

var module = angular.module("example", []);

module.controller("FormExample", function() {
  this.username = "World";
});

<div ng-controller="FormExample as ctrl">
  <input ng-model="ctrl.username"> Hello {{ctrl.username}}!
</div>

At the surface Angular 1 example looks simpler that what we have just done in Angular 2. Let's talk about the challenges of Angular 1 approach with real world applications. 
  • You can not unit test the form behavior without compiling the associated template. This is because the template contains part of the application behavior.
  • Though you can do dynamically generated data-driven forms in Angular 1, it is not easy and we want to enable this as a core use case for Angular 2.
  • The ng-model directive was built using a generic two-way data-binding which makes it difficult to reason about your template statically. Will go into depth on this topic in a following blog post and describe how it lets us achieve significant performance improvements.
  • There was no concept of an atomic form which could easily be validated, or reverted to original state. 
Although Angular 2 uses an extra level of indirection, it grants major benefits. The control object decouples form behavior from the template, so that you can test it in isolation. Tests are simpler to write and faster to execute. 

Now let's look at a more complex example so that you can see some of these properties.

Forms Mental Model

In Angular 2 working with Forms is broken down to three layers. At the very bottom we can work with HTML elements. On top of that Angular 2 provides the Form Controls API which we have shown in the example above. Finally, Angular 2 will also provide a data-driven forms API which will make building large-scale forms a snap.



Let's look at how this extra level of indirection benefits us for more realistic examples.

Data-Driven Forms

Let's say we would like to build a form such as this:



We will have an object model of an Address which needs to be presented to the user. The requirements also include providing correct layout, labels, and error handling. We would write it like this in Angular 2. 

(This is proposed API, we would love to take your input on this.)

import {forms, required, materialDesign} from 'angular2/forms';

// Our model
class Address {
  street: string;
  city: string;
  state: string;
  zip: string;
  residential: boolean;

@Component({
  selector: 'form-example'
})
@Template({
  // Form layout is automatic from the structure
  inline: `<form [form-structure]=”form”></form>`
  directives: [forms]
})
class FormExample {
  constructor(fb: FormBuilder) {
    this.address = new Address();

    // defining a form structure and initializing it using 
    // the passed in model
    this.form = fb.fromModel(address, [
      // describe the model field, labels and error handling
      {field: 'street', label: 'Street', validator: required},
      {field: 'city', label: 'City', validator: required},
      {field: 'state', label: 'State', size: 2, 
              validator: required},
      {field: 'zip', label: 'Zip', size: 5, 
              validator: zipCodeValidator},
      {field: 'isResidential', type: 'checkbox', 
              label: 'Is residential'}
    }, {
      // update the model every time an input changes
      saveOnUpdate: true,
      // Allow setting different layout strategies
      layoutStrategy: materialDesign
    });
  }
}

function zipCodeValidator(control) {
  if (! control.value.match(/\d\d\d\d\d(-\d\d\d\d)?/)){
    return {invalidZipCode: true};
  }
}

The above example shows how an existing model Address can be turned into a form. The process include describing the fields, labels, and validators in a declarative way in your component. The form HTML structure will be generated automatically based on the layout strategy provided to the builder. This helps keeping consistent look & feel through the application. Finally, it is also possible to control the write through behavior, which allows atomic writes to the model. 

We have taken great care to ensure that the forms API is pluggable, allowing you to define custom validators, reuse web-component controls, and define layout and theme.

Form Controls

Although having data driven forms is convenient, sometimes you would like to have full control of how a form is laid out on page. Let's rebuild the same example using the lower level API, which allows you to control the HTML structure in full detail. (This works today in Angular 2 Alpha, but we are also happy to receive your input on improvements we could make.)

import {forms, required} from 'angular2/forms';

// An example of typical model
class Address {
  street: string;
  city: string;
  state: string;
  zip: string;
  residential: boolean;

function zipCodeValidator(control) {
  if (! control.value.match(/\d\d\d\d\d(-\d\d\d\d)?/)){
    return {invalidZipCode: true};
  }
}

@Component({
  selector: 'form-example'
})
@Template({
  inline: `
    // explicitly defining the template of the form 
    <form [form]=”form”>
      Street <input control="street">
      <div *if="form.hasError('street', 'required')">Required</div>

      City <input control="city">
      <div *if="form.hasError('city', 'required')">Required</div>

      State <input control="state" size="2">
      <div *if="form.hasError('state', 'required')">Required</div>

      Zip <input control="zip" size="5">
      <div *if="form.hasError('zip', 'invalidZipCoed')">
        Zip code is invalid
      </div>

      Residential <input control="isResidential" type="checkbox">
    </form> 
  `
  directives: [forms]
})
class FormExample {
  constructor(fb: FormBuilder) {
    this.address = new Address();

    // defining a form model 
    this.form = fb.group({
      street: [this.address.street, required],
      city: [this.address.city, required],
      state: [this.address.city, required],
      zip: [this.address.zip, zipCodeValidator],
      isResidential: [this.address.isResidential]
    });

    this.form.changes.forEach(() => this.form.writeTo(this.address));
  }
}

When using the control form API we still define the behavior of the form in the component, but the way the form is laid out is defined in the template. This allows you to implement even the most unusual forms. 

Summary

The new Angular 2 Forms module makes building forms easier than ever. It also adds benefits such as consistent look and feel for all forms in your application, easier unit testing, and more predictable behavior.

The API is still work in progress, we welcome any feedback. Please submit your input by filing issues or creating PRs at http://github.com/angular/angular.

Wednesday, March 18, 2015

New Angular Releases - 1.3.15 and 1.4.0-beta.6

After a short hiatus due to the excitement of ng-conf, we are excited to announce a new pair of releases.

The 1.3 branch has a new point release 1.3.15 locality-filtration with a bunch of bug fixes.
The 1.4 branch is now closing in on a release candidate with what we hope will be the last beta 1.4.0-beta.6 cookie-liberation.
Some of the highlights of the new beta release include:
  • There has been a complete overhaul of ngCookies thanks to Shahar Talmi. This has a few breaking changes so be sure to check those out before you upgrade if you are using $cookies.
  • Also thanks to Shahar you can now set the timezone for a ngModel.
  • In keeping with their native counterparts, $timeout and $interval now accept additional parameters, which are passed through to the callback handler.
  • ngMock now includes new they helpers. It also now patches $controller to make it easier to create controllers that are already bound to properties, which is useful when testing controllers that are used with bindToController.
Thanks as always to everyone who has contributed to these releases:
Amy, Anthony Zotti, Brian Ford, Caitlin Potter, Casey Howard, Ciro Nunes, Dav, Dave Jeffery, Devyn Stott, Diego, Edward Delaporte, Elliot Bentley, Izhaki, Jason Bedard, Josh Kramer, Julie Ralph, Marcin Wosinek, Marcy Sutton, Martin R. Hufsky, Martin Staffa, Matias Niemelä, Michel Boudreau, Pawel Kozlowski, Peter Bacon Darwin, Rouven Weßling, Santi Albo, Shahar Talmi, Steve Mao, b0ri5, bborowin, eeue56, gdi2290, jmarkevicius, robiferentz, rodyhaddad, svershin

Tuesday, March 10, 2015

Announcements from ng-conf

We're astounded by the work the ng-conf organizers did this year in creating an amazing experience for all attendees.  Thanks for joining us!  We all had a wonderful time with you, hearing about your experiences on Angular and getting your input on the future.

In addition to the folks who came in person, more than 8000 people watched from ng-conf extended parties from 150 sites in 29 countries.  Thanks for tuning in!

If you haven’t had the time to watch through all of the videos from ng-conf last week, here’s a summary of what we announced.

In Brief

Angular 2: It is screaming fast, simpler to learn, and you can mix and match it with your existing Angular 1 applications.

Angular 1: Version 1.4 will be out with a release candidate in a week or so with many exciting new features.  The theme of 1.5 will be supporting integration with Angular 2.

AtScript is TypeScript: We’ve been collaborating with the TypeScript team on our proposed productivity extensions to JavaScript and have converged development efforts.  Details from the TypeScript team are on their blog.  Many thanks to the TypeScript team!

Details

All videos from the talk are available on the ng-conf 2015 playlist on YouTube.  Closed captions are in the works and will be added soon.

In the Day 1 Keynote, we announced the following:

  • We’re nearly done with Angular 1.4 which will include better change detection performance, improvements to many APIs, support for the new router, and new i18n.
  • Angular Material is nearing 1.0, contains 31 of the most useful material design UI elements with only a few to go.
  • We’ll continue building releases in the Angular 1 branch until the vast majority of folks have moved to Angular 2.  We want the burden to be on the Angular team to make Angular 2 simple to learn, attractive in its features, and an easy target to migrate to from Angular 1.
  • The Angular 1.5 release will start planning immediately after Angular 1.4 ships.  We’ll take community pull requests, issues, and comments to form its direction.  A primary theme will be support to make for easy migration to Angular 2.
  • We’ve had a great partnership with the TypeScript team to date.  They’ve implemented the features we care most about from AtScript.  Based on this convergence, we’ve decided to retire the AtScript name and call it all TypeScript.
  • We’ll work with them and many other partners to drive these features into standards via the TC39 process.
  • We officially support Angular 2 development in today’s JavaScript (ECMAScript 5), ES6, TypeScript, and Dart.  Angular 2, like Angular 1, will additionally work with other compile-to-JavaScript languages like CoffeeScript and ClojureScript.
  • Angular 2 is now in alpha release and you can see a preview of its new website at angular.io with its features, quickstart tutorial, and links to resources.
  • Angular 2 dramatically simplifies the great developer productivity story we have in Angular 1. 
  • By improving templating idioms, we’ve generalized for 35 directives, now part of Angular 1, into a single concept in Angular 2.  
  • We’ve eliminated many surprises and corner cases like having to call $apply when using external libraries.
  • Angular 2 builds on new web standards like ES6 and Web Components. This means that Angular 2 works seamlessly with the libraries also built on these standards, including Polymer, X-Tag, and others.  
  • We’ve dramatically improved performance in Angular 2.  Change detection is up to 10x faster.  We cache view rendering making overall performance in virtual scrolling and re-visiting views incredibly fast.
  • Angular 2 is similarly better in terms of memory efficiency -- critical for mobile devices.
  • Performance gets even better for developers who use immutable data structures or observable objects.


For details on how this all works, please also check out the Day 2 Keynote by Misko and Rado, the Components talk by Kara and Rachael from OpenTable, and Change Detection Reinvented from Victor.

We're looking forward to your input on all of this through GitHub, Twitter, and G+.  On to the rest of 2015!