1

我正在考虑使用 Angular Material 自动完成模块,就像他们在这里有一个示例一样。他们在概述中说可以使用模板驱动的表单。所以我想我会试一试。

模板:

<form (ngSubmit)="doSmth()" #f="ngForm">
    <mat-form-field>
        <input type="text" required id="name" (ngModel)="myControl" name="name" matInput [matAutocomplete]="auto">
            <mat-autocomplete #auto="matAutocomplete">
                <mat-option *ngFor="let option of filteredOptions | async" [value]="option">{{ option }}</mat-option>
            </mat-autocomplete>
    </mat-form-field>
    <button type="submit" [disabled]="!f.form.valid">Click</button>
</form>

零件:

@Component({
  selector: 'autocomplete-filter-example',
  templateUrl: 'autocomplete-filter-example.html',
  styleUrls: ['autocomplete-filter-example.css'],
})
export class AutocompleteFilterExample implements OnInit {
  myControl: FormControl();
  options: string[] = ['One', 'Two', 'Three'];
  filteredOptions: Observable<string[]>;

  ngOnInit() {
    this.filteredOptions = this.myControl.valueChanges
      .pipe(
        startWith(''),
        map((value: string) => this.filter(value))
      );
  }

  private filter(value: string): string[] {
    const filterValue = value.toLowerCase();

    return this.options.filter(option => option.toLowerCase().includes(filterValue));
  }
}

麻烦的是,当我运行它时,我得到一个错误:

AppComponent.html:1 ERROR TypeError: Cannot read property 'valueChanges' of undefined

所以我猜 ngModel 是在 init 之后绑定的?或者为什么这不起作用?我需要使用单独的实例FormControl吗?

4

2 回答 2

1

如果您想使用ngModel模板驱动的表单而不是表单控件,您可以将它与ngModelChange. 如果您想使用 observables 或纯字符串数组,则取决于您。这是使用纯字符串数组:

<mat-form-field>
  <input type="text" [ngModel]="myControl" name="name" matInput [matAutocomplete]="auto" (ngModelChange)="doFilter($event)" required>
  <mat-autocomplete #auto="matAutocomplete">
    <mat-option *ngFor="let option of filteredOptions" [value]="option">
      {{ option }}
    </mat-option>
  </mat-autocomplete>
</mat-form-field>

最初我们可以给出filteredOptions的值options。让我们使用扩展运算符不在两个数组之间创建引用:

options: string[] = ['One', 'Two', 'Three'];
filteredOptions = [... this.options];
myControl = '';

然后doFilter()函数看起来像这样:

doFilter(value) {
  const filterValue = value.toLowerCase();
  this.filteredOptions = this.options.filter(option => option.toLowerCase().includes(filterValue));
}

演示:StackBlitz

如果您不需要模板驱动的表单,您可以在没有表单标签的情况下简单地执行此操作。

如前所述,如果您将来计划发出 http 请求以获取自动完成的数据,您也可以使用 observables。如果此时使用 observables 会更明智,因为它很容易被替换。如果是这样,这是使用 observables 而不是字符串数组的选项:

import { Observable, of } from 'rxjs';

// ...

filteredOptions = of(this.options);

doFilter(value) {
  const filterValue = value.toLowerCase();
  this.filteredOptions = of(this.options.filter(option => option.toLowerCase().includes(filterValue)));
}

并在视图中使用异步管道:

<mat-option *ngFor="let option of filteredOptions | async" [value]="option">

演示:StackBlitz

于 2019-08-26T09:17:52.690 回答
0

FormControl 用于响应式表单。你可以试试下面的代码。

myControl: any;
@ViewChild('f') ngForm: NgForm;

ngOnInit() {
  this.subscription = this.ngForm.form.valueChanges.subscribe(x => {
    console.log(x);
  })
}

不要忘记取消订阅。此外,您可能需要使用 [(ngModel)] 而不是 (ngModel)。

于 2019-08-25T20:30:43.693 回答