In der Tat gibt es zwei Dinge zu implementieren:
- Eine Komponente, die die Logik Ihrer Formularkomponente bereitstellt. Es ist keine Eingabe, da es von
ngModel
selbst 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 TagsComponent
Komponente definiert die Logik zum Hinzufügen und Entfernen von Elementen in der tags
Liste.
@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 setValue
Eins (der Name ist hier nicht wichtig). Wir verwenden es später, um den Wert von der ngModel
zur 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 ControlValueAccessor
Schnittstelle implementiert . Für diesen Wert-Accessor muss ein Provider für das NG_VALUE_ACCESSOR
Token definiert werden (vergessen Sie nicht, ihn zu verwenden, forwardRef
da die Direktive danach definiert ist).
Die Direktive fügt dem tagsChange
Ereignis des Hosts einen Ereignis-Listener hinzu (dh die Komponente, an die die Direktive angehängt ist, dh die TagsComponent
). Die onChange
Methode 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 writeValue
wird aufgerufen, wenn der in gebundene Wert ngForm
aktualisiert wird. Nachdem Sie die angehängte Komponente (dh TagsComponent) eingefügt haben, können Sie sie aufrufen, um diesen Wert zu übergeben (siehe vorherige setValue
Methode).
Vergessen Sie nicht, die CUSTOM_VALUE_ACCESSOR
in 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 tags
wird das valid
Attribut des companyForm.controls.tags
Steuerelements false
automatisch , wenn ich das gesamte Unternehmen entferne .
Weitere Informationen finden Sie in diesem Artikel (Abschnitt "NgModel-kompatible Komponente"):