我尝试将mat-chips和mat-autocomplete包装到ControlValueAccesor中,但是出现以下错误:
InfoEditorComponent.html:17 ERROR TypeError: control.registerOnChange is not a function
at setUpModelChangePipeline (forms.js:2701)
at setUpControl (forms.js:2580)
at FormGroupDirective.addControl (forms.js:6318)
at FormControlName._setUpControl (forms.js:6969)
at FormControlName.ngOnChanges (forms.js:6892)
at checkAndUpdateDirectiveInline (core.js:24499)
at checkAndUpdateNodeInline (core.js:35163)
at checkAndUpdateNode (core.js:35102)
at debugCheckAndUpdateNode (core.js:36124)
at debugCheckDirectivesFn (core.js:36067)
KeywordsEditorComponent.html:11 ERROR Error: Cannot find form control at index 2
at FormArray._throwIfControlMissing (forms.js:4916)
at forms.js:4711
at Array.forEach (<anonymous>)
at FormArray.setValue (forms.js:4705)
at updateControl (forms.js:2691)
at KeywordsEditorComponent.onChange (forms.js:2663)
at KeywordsEditorComponent.createKeyword (keywords-editor.component.ts:88)
at KeywordsEditorComponent.add (keywords-editor.component.ts:50)
at Object.eval [as handleEvent] (KeywordsEditorComponent.html:19)
at handleEvent (core.js:34789)
这是必须从https://material.angular.io/components/chips/overview#chip-input
复制的包装器import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { Component, ElementRef, ViewChild, forwardRef, OnInit } from '@angular/core';
import { MatAutocompleteSelectedEvent, MatAutocomplete } from '@angular/material/autocomplete';
import { MatChipInputEvent } from '@angular/material/chips';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { FormControl, ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
export const KEYWORDS_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => KeywordsEditorComponent),
multi: true,
};
@Component({
selector: 'app-keywords-editor',
template: `<mat-form-field class="chip-list">
<mat-chip-list #chipList aria-label="Keywords">
<mat-chip
*ngFor="let keyword of keywords"
[selectable]="selectable"
[removable]="removable"
(removed)="remove(keyword)">
{{keyword}}
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
</mat-chip>
<input
placeholder="New keywords..."
#keywordInput
[formControl]="keywordCtrl"
[matAutocomplete]="auto"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="addOnBlur"
(matChipInputTokenEnd)="add($event)">
</mat-chip-list>
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
<mat-option *ngFor="let keyword of filteredKeywords | async" [value]="keyword">
{{keyword}}
</mat-option>
</mat-autocomplete>
</mat-form-field>`,
styleUrls: ['./keywords-editor.component.sass'],
providers: [KEYWORDS_VALUE_ACCESSOR]
})
export class KeywordsEditorComponent implements ControlValueAccessor {
visible = true;
selectable = true;
removable = true;
addOnBlur = true;
separatorKeysCodes: number[] = [ENTER, COMMA];
keywordCtrl = new FormControl();
filteredKeywords: Observable<string[]>;
keywords: string[] = ['Lemon'];
allKeywords: string[] = ['Apple', 'Lemon', 'Lime', 'Orange', 'Strawberry'];
onChange;
@ViewChild('keywordInput', {static: false}) keywordInput: ElementRef<HTMLInputElement>;
@ViewChild('auto', {static: false}) matAutocomplete: MatAutocomplete;
constructor() {
this.filteredKeywords = this.keywordCtrl.valueChanges.pipe(
startWith(null),
map((keyword: string | null) => keyword ? this._filter(keyword) : this.allKeywords.slice()));
}
add(event: MatChipInputEvent): void {
// Add keyword only when MatAutocomplete is not open
// To make sure this does not conflict with OptionSelected Event
if (!this.matAutocomplete.isOpen) {
const input = event.input;
const value = event.value;
// Add our keyword
this.createKeyword(value);
// Reset the input value
if (input) {
input.value = '';
}
this.keywordCtrl.setValue(null);
}
}
remove(keyword: string): void {
const index = this.keywords.indexOf(keyword);
if (index >= 0) {
this.keywords.splice(index, 1);
}
}
selected(event: MatAutocompleteSelectedEvent): void {
this.createKeyword(event.option.viewValue);
this.keywordInput.nativeElement.value = '';
this.keywordCtrl.setValue(null);
}
private _filter(value: string): string[] {
const filterValue = value.toLowerCase();
return this.allKeywords.filter(keyword => keyword.toLowerCase().indexOf(filterValue) === 0);
}
createKeyword(keyword: string) {
if (!keyword) {
return;
}
let key = keyword.trim()
if (key !== '' && this.keywords.indexOf(keyword) === -1) {
this.keywords.push(keyword)
this.onChange(this.keywords)
}
}
writeValue(obj: any): void {
this.keywords = obj;
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
throw new Error("Method not implemented.");
}
setDisabledState?(isDisabled: boolean): void {
throw new Error("Method not implemented.");
}
}
使用具有自动完成功能的芯片的父母表格:
import { Component, OnInit, Input } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { Observable } from 'rxjs';
@Component({
selector: 'app-info-editor',
templateUrl: `<form [formGroup]="formGroup">
<app-keywords-editor formControlName="keywords"></app-keywords-editor>
</form>`,
styleUrls: ['./info-editor.component.sass']
})
export class InfoEditorComponent implements OnInit {
formGroup: FormGroup = this.fb.group({
keywords: this.fb.array(['a','b'])
})
constructor(
private fb: FormBuilder) {
}
}
答案 0 :(得分:0)
我想你应该替换
formGroup: FormGroup = this.fb.group({
keywords: this.fb.array(['a','b'])
})
使用
formGroup: FormGroup = this.fb.group({
keywords: ['a','b']
})
关键字已经是一个应该传递给controll的值,而不是一个controlls数组