第一次单击焦点时开始

时间:2018-07-23 21:36:51

标签: angular rxjs6 angular-material-5

使用“自动完成”功能从API中提取城市,然后允许用户搜索旅行。 问题是,即使我使用的是startWith,我也必须先在该字段中单击,然后开始键入内容才能正常工作,但是当用户将注意力集中在该输入框上时,我无法立即显示下拉菜单。 作为解决方案,我想在填充城市变量的订阅之后调用它。我该怎么做?该列表是否应可观察?然后继续订阅?

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

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


  destinationCity: FormControl =  new FormControl();
  originCity: FormControl =  new FormControl();
  startDate: FormControl = new FormControl();



  constructor(public cityService: CityService) {}


  ngOnInit() {
    this.cityService.getCities();
    this.citiesSub = this.cityService
      .getCityUpdateListener()
      .subscribe(cities => {
        this.cities = cities;
    });
    this.currentCity = this.destinationCity.valueChanges
    .pipe(
      debounceTime(100),
      startWith(''),
      map(x=>{
        return this._filter(x);
      }
    ));
  }
private _filter(value: string): City[]{
  const filterValue = value.toLowerCase();
  return(this.cities.filter(option => option.name.toLowerCase().includes(filterValue)));
}

  ngOnDestroy() {
    this.citiesSub.unsubscribe();
  }
}
<mat-card>
  <form (submit)="onLogin(instantFlight)" #instantFlight="ngForm">
    <mat-form-field>
      <input  type="text" id="destinationCity" name="destinationCity" matInput [formControl]="destinationCity" [matAutocomplete]="autoDestination">

      <mat-autocomplete #autoDestination="matAutocomplete">
        <mat-option *ngFor="let c of currentCity | 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 [formControl]="originCity" [matAutocomplete]="autoOrigin">

    <mat-autocomplete #autoOrigin="matAutocomplete">
      <mat-option *ngFor="let c of cities" [value]="c.code">
        {{c.name}} - {{c.code}}
      </mat-option>
    </mat-autocomplete>
  </mat-form-field>
  <mat-form-field>
      <input matInput id="startDate" name="startDate" [formControl]="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>

代码已更新

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

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

  destinationCity: FormControl =  new FormControl();
  originCity: FormControl =  new FormControl();
  startDate: FormControl = new FormControl();

  constructor(public cityService: CityService) {}

  ngOnInit() {
    this.cityService.getCities();
    this.testCities = this.cityService
      .getCityUpdateListener();

    this.currentCity = this.destinationCity.valueChanges
    .pipe(
      withLatestFrom(this.testCities),
      debounceTime(100),
      map((x) =>{
       return this._filter(x);
            }
    ));

  }

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

  ngOnDestroy() {
    this.citiesSub.unsubscribe();
  }
}

2 个答案:

答案 0 :(得分:1)

在这种情况下,startWith实际上将发出该空字符串值和您的map函数,但是在分配this.cities之前已经完成了第一次发射。实际上,下一次发射是valueChanges再次发射时。

因此,当第一个map Observable发出时,我们可以运行该cities方法。实际上,我们只想在发出任一 Observable时运行该map方法。我们可以通过一点重构和withLatestFrom来实现:

  ngOnInit() {
    this.cityService.getCities();
    this.cities = this.cityService.getCityUpdateListener();

    this.currentCity = this.destinationCity.valueChanges
    .pipe(
      debounceTime(100),
      withLatestFrom(this.cities)
      map([value, cities] => cities.filter(s => s.name.toLowerCase().includes(value.toLowerCase)));
    ));

  }

withLatestFrom将等待给定的Observable发出至少一个值,然后再继续流。由于此处的Observable较慢,因此map函数仅在发出某些内容后才运行。它还从两个可观察值发出一个成对的值,因此进行了一些分解处理。

我们也可以更改您的_filter函数以接受一个cities参数,或者只进行内联过滤器,因为我们已经没有this.cities静态数组值了。我喜欢第二种方法,因为它可以使所有数据与一个流中包含的流相关。

此外,在cities上重复执行此更改时,还需要标记中的异步管道。不过,这很好,因为async管道会自动处理退订。

答案 1 :(得分:0)

int i;
for(i=0;i<=10;i++)
{
    ...
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, Validators, NgForm } 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[]>;
  private citiesSub: Subscription;
  currentCity: Observable<City[]>;
  testCities: Observable<City[]>;

  destinationCity: FormControl =  new FormControl();
  originCity: FormControl =  new FormControl();
  startDate: FormControl = new FormControl();

  constructor(public cityService: CityService) {}

 ngOnInit() {
  this.cityService.getCities();
  this.cities = this.cityService.getCityUpdateListener();
  this.currentCity = this.destinationCity.valueChanges
    .pipe(
      withLatestFrom(this.cities),
       debounceTime(100),
      map(
        ([first, second]) =>{
       return this._filter(first,second);
            }
    )
  );
 }

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

  }