angular 中具有自动完成功能的表单生成器 6

form builder with autocomplete in angular 6

我有两套代码可以工作,但我无法按照我的意愿工作。所以我希望获得一些概念性的见解。

我有一个从 API 抓取城市的服务。该服务自动完成过滤结果。当我使用 form builder 时,我无法获取要过滤的数据,因为它没有执行 fromLatestWith

我正在使用 的最新版本 来同时使用可观测值 valueChange 和引入城市的服务。当我使用 formControl 但不使用 formBuilder 时它有效。

当我不使用 fromLatestWith 时,我可以让服务和自动完成工作,但是,由于服务首先加载,我必须在自动完成下拉列表显示之前输入。

理想情况下,我想要使用 formbuilder 并在初始化时使用 自动完成下拉菜单 。我错过了什么?

以下是两个实例的副本:

//using formBuilder with latestWith lets me see valueChange but doesnt
//load cities if I don't use formBuilder and use formControl it works

import { CityService } from "../services/city-list.service";
import { Component, OnInit, OnDestroy } from "@angular/core";
import { City } from "../models/city";
import { Subscription, Observable } from "rxjs";
import {
  map,
  filter,
  startWith,
  withLatestFrom,
  debounceTime
} from "rxjs/operators";
import {
  FormGroup,
  FormControl,
  FormBuilder,
  Validators,
  NgForm,
  Form
} from "@angular/forms";

@Component({
  selector: "app-city-list",
  templateUrl: "./city-list.component.html",
  styleUrls: ["./city-list.component.css"]
})
export class CityListComponent implements OnInit {
  cities: Observable<City[]>;

  destinationCitySub: Observable<City[]>;
  originCitySub: Observable<City[]>;

  instantFlightForm: FormGroup;


  constructor(public cityService: CityService, private fb: FormBuilder) {

  }

  ngOnInit() {
    this.instantFlightForm = this.fb.group({
      destinationCity: [""],
      originCity: ["", Validators.required],
      startDate: []
    });



    this.cityService.getCities();
    this.cities = this.cityService.getCityUpdateListener();
   this.destinationCitySub = this.valueChange('destinationCity');

  }

  private valueChange(string) {
    console.log(string);
    return this.instantFlightForm.get(string).valueChanges.pipe(
      withLatestFrom(this.cities),
      debounceTime(100),
      map(([term, city]) => {
        console.log(term);
        return this._filter(term, city);
      })
    );
  }

  private _filter(first, second): City[] {
    const filterValue = first.toLowerCase();
    return second.filter(option =>
      option.name.toLowerCase().includes(filterValue)
    );
  }

  onInstantSearch(form: FormGroup) {
    console.log(form.value);
  }
}
<mat-card>

  <form [formGroup]="instantFlightForm" (submit)="onInstantSearch(instantFlightForm)">
    <mat-form-field>
      <input  type="text" id="destinationCity" name="destinationCity" matInput formControlName="destinationCity" [matAutocomplete]="autoDestination">

      <mat-autocomplete #autoDestination="matAutocomplete">
        <mat-option *ngFor="let c of destinationCitySub|async" [value]="c.code">
          {{c.name}} - {{c.code}}
        </mat-option>
      </mat-autocomplete>
    </mat-form-field>
    <mat-form-field>
    <input  type="text" id="originCity" name="originCity" matInput formControlName="originCity" [matAutocomplete]="autoOrigin">

    <mat-autocomplete #autoOrigin="matAutocomplete">
      <mat-option *ngFor="let c of originCitySub|async" [value]="c">
        {{c}}
      </mat-option>
    </mat-autocomplete>
  </mat-form-field>
  <mat-form-field>
      <input matInput id="startDate" name="startDate" formControlName="startDate" [matDatepicker]="picker" placeholder="Choose a date">
      <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
      <mat-datepicker #picker></mat-datepicker>
    </mat-form-field>
    <button mat-raised-button type="submit" color="accent">Search</button>
  </form>
</mat-card>

//using it without latestWith - lets me autocomplete but requires
//typing first

import { CityService } from "../services/city-list.service";
import { Component, OnInit, OnDestroy } from "@angular/core";
import { City } from "../models/city";
import { Subscription, Observable } from "rxjs";
import {
  map,
  filter,
  startWith,
  withLatestFrom,
  debounceTime
} from "rxjs/operators";
import {
  FormGroup,
  FormControl,
  FormBuilder,
  Validators,
  NgForm,
  Form
} from "@angular/forms";

@Component({
  selector: "app-city-list",
  templateUrl: "./city-list.component.html",
  styleUrls: ["./city-list.component.css"]
})
export class CityListComponent implements OnInit {
  cities: City[];
citySub: Subscription;
  destinationCitySub: Observable<City[]>;
  originCitySub: Observable<City[]>;

  instantFlightForm: FormGroup;

  constructor(public cityService: CityService, private fb: FormBuilder) {

  }

  ngOnInit() {
    this.instantFlightForm = this.fb.group({
      destinationCity: [""],
      originCity: ["", Validators.required],
      startDate: []
    });



    this.cityService.getCities();
    this.citySub = this.cityService.getCityUpdateListener().subscribe(c=>this.cities=c);
   this.destinationCitySub = this.valueChange('destinationCity');

  }

  private valueChange(string) {
    return this.instantFlightForm.get(string).valueChanges.pipe(
     // withLatestFrom(this.cities),
      debounceTime(100),
      map((term) => {
        console.log(term);
        return this._filter(term);
      })
    );
  }

  private _filter(term): City[] {
    const filterValue = term.toLowerCase();
    return this.cities.filter(option =>
      option.name.toLowerCase().includes(filterValue)
    );
  }

  onInstantSearch(form: FormGroup) {
    console.log(form.value);
  }
}

withLatestFrom 在可观察流上运行,而不是值。您需要将 cities 设置为 observable 并确保它至少触发一次。

this.cities = this.cityService.getCityUpdateListener();