Eliya Cohen Eliya Cohen - 2 months ago 19
Javascript Question

Angular 2 update data when another data has been updated

I have two

<select>
elements, one for the Districts and the other one for the Cities. I get the districts by calling the function
getDistricts()
from
ngOnInit()
function. So far so good.

I don't know how to tell angular to fetch the new cities to the cities' select element. I tried to do like in the code below, but I've got an error:

ORIGINAL EXCEPTION: TypeError: Cannot read property 'cities' of undefined


It happens of course because the
selectedDistrict
is undefined in the start.




This is an example of a district object. (it includes the cities in it).

{
"id": 5,
"name": "A district",
"cities": [
{
"id": 59,
"name": "City 1"
},
{
"id": 60,
"name": "City 2"
}
]
},


district.service.ts:

import {Injectable} from "@angular/core";
import 'rxjs/add/operator/toPromise';

import {District} from "./district";
import {HttpClientService} from "../http-client/http-client.service";

@Injectable()
export class DistrictService {

private districtsUrl = 'districts/all';

constructor(private httpClient: HttpClientService) {
this.httpClient = httpClient;
}

getDistricts(): Promise<District[]> {
return this.httpClient.get(this.districtsUrl)
.toPromise()
.then(response => response.json().data)
.catch(this.handleError);
}

getDistrict(district: string): Promise<District> {
return this.getDistricts()[district];
}

private handleError(error: any) {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
}
}


view-search.component.ts:

export class ViewSearchComponent implements OnInit {
districts: district[];
selectedDistrict: district;

constructor(private districtService: districtService) {}

ngOnInit() {
this.getDistricts();
}

getDistricts() {
return this.districtService.getDistricts().then(districts => this.districts = districts);
}

selectDistrict(district: district) {
this.selectedDistrict = district;
}
}


view.search.component.html

<select class="search-select">
<option *ngFor="let district of districts" (click)="selectDistrict(district)">
{{ district.name }}
</option>
</select>

<select class="search-select">
<option *ngFor="let city of selectedDistrict.cities ">
{{ city.name }}
</option>
</select>

Answer

Very few people know that Angular2 supports elvis operator (i.e. ?) in templates which is extremely useful for asynchronous data streams. For that, you have to update the template as,

<select class="search-select">
    <option *ngFor="let city of selectedDistrict?.cities ">
        {{ city.name }}
    </option>
</select>