代码之家  ›  专栏  ›  技术社区  ›  Pari Baker

从第一次点击焦点开始

  •  2
  • Pari Baker  · 技术社区  · 6 年前

    使用自动完成功能,从API中提取城市,然后允许用户搜索行程。 问题是,尽管我使用的是StartWith,但我首先必须单击该字段,然后开始键入以使其工作,但当用户关注该输入框时,我无法让下拉列表立即显示出来。 作为一个解决方案,我希望在填充cities变量的订阅之后调用它。我该怎么做?这个列表应该是可观察的吗?然后继续订阅?

    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 回复  |  直到 6 年前
        1
  •  1
  •   joh04667    6 年前

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

    所以,我们可以运行它 map 方法当第一个 cities 可观测发射代替。实际上,我们只是想运行它 地图 方法 任何一个 可观测发射。我们可以通过一点重构和 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)));
        ));
    
      }
    

    最晚从 在继续流之前,将等待给定的Observable发出至少一个值。因为这里观测到的速度较慢, 地图 函数只在它发出某个东西后才运行。它也会从两个观测点发出一个成对的值,所以一些破坏处理了这个问题。

    我们也可以改变你的 _filter 接受函数 城市 参数或直接执行过滤器内联,因为我们没有 这个城市 静态数组值。我喜欢第二种方法,因为它将与包含在一个流中的流相关的所有数据都保存在一个流中。

    此外,此更改在上重复时需要标记中的异步管道 城市 . 不过这很好,因为 async 管道自动处理取消订阅。

        2
  •  0
  •   Pari Baker    6 年前

    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)));
    }
    
      }
    <mat-card>
      <form #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 currentCity|async" [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>
    推荐文章