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();
我有两套代码可以工作,但我无法按照我的意愿工作。所以我希望获得一些概念性的见解。
我有一个从 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();