Working on AutoComplete that pulls cities from an API and then allows users to search for trips. The problem is, even though I am using startWith, I first have to click in the field and then start typing for it to work but I can't get the drop down to immediately show as the user focuses on that input box. As a solution I want to call it after the subscription that populates the cities variable. How would I do this? Should the list as an observable? and then go ahead and resubscribe?
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>
WITH UPDATED CODE
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();
}
}