Eliya Cohen
Eliya Cohen

Reputation: 11458

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>

Upvotes: 0

Views: 126

Answers (4)

codef0rmer
codef0rmer

Reputation: 10520

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>

Upvotes: 1

Eliya Cohen
Eliya Cohen

Reputation: 11458

Instead of initializing my selectedDistrict, I just had to add *ngIf="selectedDistrict" to my select element.

Upvotes: 0

null canvas
null canvas

Reputation: 10603

Try to initialize your selectedDistrict: district;

selectedDistrict: district = {};

also for selecting an option I don't think this is a good idea (click)="selectDistrict(district)" because you could theoretically use the keyboard/arrows to select an item

so you should use the onchange event

<select (change)="alert(this.options[this.selectedIndex].text);">

Upvotes: 1

Tawfiq abu Halawah
Tawfiq abu Halawah

Reputation: 1234

try to initialize the districts: district[]; array you can do that like:

districts = [];

the problem is that you are trying to access city property of an uninitialized object. you need to make sure that you districts objects are initialized.

for example using new

this.selectedDistrict = new District();

or

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

also in this code

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

make sure that selectDistrict is called correctly

Upvotes: 0

Related Questions