In der Tat gibt es zwei Dinge zu implementieren:
- Eine Komponente, die die Logik Ihrer Formularkomponente bereitstellt. Es ist keine Eingabe, da es von
ngModelselbst bereitgestellt wird
- Ein Benutzer
ControlValueAccessor, der die Brücke zwischen dieser Komponente und ngModel/ implementiertngControl
Nehmen wir eine Probe. Ich möchte eine Komponente implementieren, die eine Liste von Tags für ein Unternehmen verwaltet. Die Komponente ermöglicht das Hinzufügen und Entfernen von Tags. Ich möchte eine Validierung hinzufügen, um sicherzustellen, dass die Tag-Liste nicht leer ist. Ich werde es in meiner Komponente wie unten beschrieben definieren:
(...)
import {TagsComponent} from './app.tags.ngform';
import {TagsValueAccessor} from './app.tags.ngform.accessor';
function notEmpty(control) {
if(control.value == null || control.value.length===0) {
return {
notEmpty: true
}
}
return null;
}
@Component({
selector: 'company-details',
directives: [ FormFieldComponent, TagsComponent, TagsValueAccessor ],
template: `
<form [ngFormModel]="companyForm">
Name: <input [(ngModel)]="company.name"
[ngFormControl]="companyForm.controls.name"/>
Tags: <tags [(ngModel)]="company.tags"
[ngFormControl]="companyForm.controls.tags"></tags>
</form>
`
})
export class DetailsComponent implements OnInit {
constructor(_builder:FormBuilder) {
this.company = new Company('companyid',
'some name', [ 'tag1', 'tag2' ]);
this.companyForm = _builder.group({
name: ['', Validators.required],
tags: ['', notEmpty]
});
}
}
Die TagsComponentKomponente definiert die Logik zum Hinzufügen und Entfernen von Elementen in der tagsListe.
@Component({
selector: 'tags',
template: `
<div *ngIf="tags">
<span *ngFor="#tag of tags" style="font-size:14px"
class="label label-default" (click)="removeTag(tag)">
{{label}} <span class="glyphicon glyphicon-remove"
aria- hidden="true"></span>
</span>
<span> | </span>
<span style="display:inline-block;">
<input [(ngModel)]="tagToAdd"
style="width: 50px; font-size: 14px;" class="custom"/>
<em class="glyphicon glyphicon-ok" aria-hidden="true"
(click)="addTag(tagToAdd)"></em>
</span>
</div>
`
})
export class TagsComponent {
@Output()
tagsChange: EventEmitter;
constructor() {
this.tagsChange = new EventEmitter();
}
setValue(value) {
this.tags = value;
}
removeLabel(tag:string) {
var index = this.tags.indexOf(tag, 0);
if (index != undefined) {
this.tags.splice(index, 1);
this.tagsChange.emit(this.tags);
}
}
addLabel(label:string) {
this.tags.push(this.tagToAdd);
this.tagsChange.emit(this.tags);
this.tagToAdd = '';
}
}
Wie Sie sehen können, gibt es in dieser Komponente keine Eingabe, sondern eine setValueEins (der Name ist hier nicht wichtig). Wir verwenden es später, um den Wert von der ngModelzur Komponente bereitzustellen . Diese Komponente definiert ein Ereignis, das benachrichtigt werden soll, wenn der Status der Komponente (die Tags-Liste) aktualisiert wird.
Lassen Sie uns nun die Verknüpfung zwischen dieser Komponente und ngModel/ implementieren ngControl. Dies entspricht einer Direktive, die die ControlValueAccessorSchnittstelle implementiert . Für diesen Wert-Accessor muss ein Provider für das NG_VALUE_ACCESSORToken definiert werden (vergessen Sie nicht, ihn zu verwenden, forwardRefda die Direktive danach definiert ist).
Die Direktive fügt dem tagsChangeEreignis des Hosts einen Ereignis-Listener hinzu (dh die Komponente, an die die Direktive angehängt ist, dh die TagsComponent). Die onChangeMethode wird aufgerufen, wenn das Ereignis eintritt. Diese Methode entspricht der von Angular2 registrierten. Auf diese Weise werden Änderungen erkannt und das zugehörige Formularsteuerelement entsprechend aktualisiert.
Das writeValuewird aufgerufen, wenn der in gebundene Wert ngFormaktualisiert wird. Nachdem Sie die angehängte Komponente (dh TagsComponent) eingefügt haben, können Sie sie aufrufen, um diesen Wert zu übergeben (siehe vorherige setValueMethode).
Vergessen Sie nicht, die CUSTOM_VALUE_ACCESSORin den Bindungen der Richtlinie anzugeben.
Hier ist der vollständige Code des Brauchs ControlValueAccessor:
import {TagsComponent} from './app.tags.ngform';
const CUSTOM_VALUE_ACCESSOR = CONST_EXPR(new Provider(
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => TagsValueAccessor), multi: true}));
@Directive({
selector: 'tags',
host: {'(tagsChange)': 'onChange($event)'},
providers: [CUSTOM_VALUE_ACCESSOR]
})
export class TagsValueAccessor implements ControlValueAccessor {
onChange = (_) => {};
onTouched = () => {};
constructor(private host: TagsComponent) { }
writeValue(value: any): void {
this.host.setValue(value);
}
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}
Auf diese Weise tagswird das validAttribut des companyForm.controls.tagsSteuerelements falseautomatisch , wenn ich das gesamte Unternehmen entferne .
Weitere Informationen finden Sie in diesem Artikel (Abschnitt "NgModel-kompatible Komponente"):