• 10. Juni 2016

Writing custom form controls for Angular 2 [EN]

Update in January 2017: This article has been updated for the final API of Angular 2.x.

If you want your components to work with declarative (template-driven) and imperative forms, you have to implement the interface ControlValueAccessor. The methods defined by this interface permit Angular 2 to read and set the state of the control in question.

Such controls can be used with ngModel or ngControl. To illustrate this, the following example uses a custom date-control that binds a variable date with ngModel:

<!-- Deklaratives (template-driven) Forms-Handling 
<date-control [(ngModel)]="date"></date-control>

Alternatively, such a component can use imperative forms to bind to a predefined Control-object. The following example illustrates this by binding the date-control with formControlName to the FormControl with the name date. Angular expects this Control-object in the FormGroup that has been specified with formGroup:

<!-- Imperatives Forms-Handling -->
<form [formGroup]="filter">	
    <date-control formControlName="date"></date-control>
    [...]
</form>

The ControlGroup and the Control are provided via the component:

@Component({
    selector: 'flight-search',  
    template: require('./flight-search.component.html'),
    directives: [DateControlComponent]
})
export class FlightSearchImpComponent {
    
    
    public filter: FormGroup;
    
    constructor(private fb: FormBuilder) {
            
            
        this.filter = fb.group({
           date: ['2016-05-01']
        });
	}
	
	[...]
}

This article describes the steps necessary to implement ControlValueAccessor. The total sample can be found here.

ControlValueAccessor

The interface ControlValueAccessor provides three methods for synchronizing the state of a control with the object graph Angular uses to represent a form:

//
// From the Angular2-Sources
//
export interface ControlValueAccessor {
    writeValue(obj: any): void;
    registerOnChange(fn: any): void;
    registerOnTouched(fn: any): void;
}

To write a value to the control, Angular uses the method writeValue. Since Angular must know about changes to the date, the SPA-flagship uses registerOnChange and registerOnTouched to register callbacks. The control has to call the first one, when the users changes the state. The latter one indicates that the field at least had the focus.

Implementing ControlValueAccessor

The following example demonstrates the implementation of the interface ControlValueAccessor. For this, it shows a simple component for editing dates. The method splitDate takes a date and breaks it down into its parts, which are offered for editing by the (here not shown) template. The method apply is putting those together to a date again.

import { Component } from '@angular/core';
import { ControlValueAccessor, NgControl } from '@angular/forms';

@Component({
    selector: 'date-control',
    template: require('./date-control.component.html')
})
export class DateControlComponent 
                    implements ControlValueAccessor {
    
    day: number;
    month: number;
    year: number;
    hour: number;
    minute: number;
    
    constructor(private c: NgControl) {
        c.valueAccessor = this;
    }

    writeValue(value: any) {
        this.splitDate(value);
    }
    
    onChange = (_) => {};
    onTouched = () => {};
    
    registerOnChange(fn): void { this.onChange = fn; }
    registerOnTouched(fn): void { this.onTouched = fn; }

    
    splitDate(dateString) {
      var date = new Date(dateString); 
      
      this.day = date.getDate();
      this.month = date.getMonth() + 1;
      this.year = date.getFullYear();
      this.hour = date.getHours();
      this.minute = date.getMinutes();
    }
    
    apply() {
        
        var date = new Date();
        date.setDate(this.day);
        date.setMonth(this.month - 1);
        date.setFullYear(this.year);
        date.setHours(this.hour);
        date.setMinutes(this.minute);
        date.setSeconds(0);
        date.setMilliseconds(0);
        
        this.onChange(date.toISOString());
        this.onTouched();
    }
    
}

To be able to interact with the Forms-Handling of Angular 2, the component implements the interface ControlValueAccessor. In addition, it gets the current NgControl by the means of Dependency Injection. Angular uses this instance to represent the control within the object graph that has been created for the form. It sets the property ValueAccessor to the component itself. This means, that the component is it's own ValueAccessor.

The implementation of writeValue takes a new value from the framework and delegates it to splitDate. The implementations of registerOnChange and registerOnTouched however take callbacks from Angular and puts them into onChange and onTouched.

After the date has been changed, the template invokes the method apply. It adds the individual parts of the date together and passes it to Angular using onChange. In addition, for the sake of completeness, it executes the calback onTouched.

 

 
Hier können Sie eine Anfrage für eine unverbindliche Schulung ode Beratung bzw. einen Workshop erstellen.
 
Unverbindliche Anfrage
 
 

Schulungen

Angular Schulung: Strukturierte Einführung

Lernen Sie in dieser interaktiven Schulung anhand einer Beispielanwendung den Einsatz von Angular für Ihre erfolgreichen Projekte kennen. Sie durchdringen die Hintergründe und bauen von der ersten Minute an auf Best Practices auf.

Details

Advanced Angular: Enterprise-Anwendungen und Architektur

In dieser Schulung erfahren Sie alles für die Entwicklung großer Anwendungen mit Angular: Mono-Repos, Micro-Apps, State Management, Performance und mehr

Details

Angular: Strukturierte Einführung

Seit der Ankündigung von Angular (2+) fragen sich Entwicklungs-Teams, welche Migrationspfade für AngularJS-1.x-Anwendungen vorliegen werden. Das im Lieferumfang von Angular enthaltene ngUpgrade bietet eine Antwort darauf. Es erlaubt einen Parallelbetrieb von AngularJS 1.x und Angular (2+) und stellt somit die Grundlage für eine schrittweise Migration dar.

Details

Progressive Web-Apps mit Angular

Progressive Web Apps bieten den Komfort nativer Anwendungen, indem sie auf moderne Browser APIs, wie Service Worker, setzen. Sie sind installierbar sowie offlinefähig und nutzen Hintergrundprozesse für Datensynchronisation und Push-Notifications. Diese Schulung zeigt anhand eines durchgehenden Beispiels was sich genau hinter diesem neuen Konzept verbirgt, wie solche Anwendungen mit Angular entwickelt werden und wie Sie in Ihren Projekten von den dahinterstehenden Ideen profitieren.

Details

Reaktive Architekturen mit Angular und Redux

Dieses interaktive Seminar vermittelt, wie Sie reaktive Anwendungen mit Angular entwickeln können.

Details

TypeScript

TypeScript gibt Ihnen alle Möglichkeiten der neuen JavaScript-Standards und zusätzlich ein statisches Typsystem, das dabei hilft, Fehler möglichst früh zu erkennen. Außerdem ist TypeScript die Grundlage für Angular. In diesem interaktiven Seminar lernen Sie diese mächtige Sprache anhand einer Fallstudie kennen.

Details

Weitere Schulungen ...