Refresh Angular 2 data after a related data update

I am facing an issue with my <select> elements in Angular. One for the Districts and another for the Cities. The districts are fetched using the getDistricts() function within the ngOnInit() function without any problems.

However, I am unsure how to dynamically update the cities based on the selected district. I attempted the following code snippet but encountered an error:

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

The error occurs because the initial value of selectedDistrict is undefined.


Below is an example object representing a district, which includes its associated cities:

{
  "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 №1

Did you know that Angular2 actually supports the elvis operator (i.e. ?) in templates? This feature is incredibly useful for dealing with asynchronous data streams. To take advantage of it, simply update your template like this:

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

Answer №2

Make sure to initialize your selectedDistrict variable as shown below:

selectedDistrict = {};

Instead of using

(click)="selectDistrict(district)"
to select an option, it's recommended to use the onchange event. This allows users to select items using keyboard arrows.

Here's an example of how you can implement it:

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

Answer №3

Make an attempt to initialize the districts: district[]; array you can accomplish this with the following code:

districts = [];

The issue arises when you try to access the city property of an uninitialized object. It is essential to ensure that your districts objects are properly initialized.

For instance, you can use the new keyword like this:

this.selectedDistrict = new District();

or

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

Additionally, in the provided code snippet:

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

ensure that selectDistrict is being called correctly.

Answer №4

To set up my chosenArea, I simply needed to include the *ngIf="selectedDistrict" directive in the select section.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Experiencing an issue with Jest - Error: unable to access property 'forEach' of null

After watching some tutorials, I decided to create a sample project in Jest for writing tests. In a TypeScript file, I included a basic calculation function like this: Calc.cs export class Calc { public add(num1: number, num2: number): number { ...

Ways to locate two div class elements that are generated dynamically

I am looking to dynamically create 2 divs in different locations. One for displaying information and another for editing information. I want to be able to delete both divs with the same class when using the delete button. Since they are located in differe ...

Material UI autocomplete is not detecting the options parameter as an array

I am currently working on implementing an autocomplete field that retrieves options from my component's state, which in turn fetches data from the backend. Here is a snippet of my component: export const Person: React.FC<PersonProps> = ({name, a ...

Creating a 3D object using three.js framework

When playing video games, I often notice a common pattern where hovering the mouse over an object triggers a gradient highlight around its 2D representation. To recreate this effect in my Three.js scene, I started by setting up the necessary raycaster vari ...

How can I use querySelector in JavaScript to target all div elements that have the same class?

I'm having an issue with the document.querySelector in my JavaScript code. It currently only selects the first div that contains the class "test", but I need it to select all such divs. Is there a way to achieve this using my existing JavaScript? ...

Collaborative service involves objects passing through reference

I am encountering an issue with a shared service in angular. Upon application startup, the init function is triggered, fetching and returning data that is vital across the entire application. Components have the ability to inject this service and retrieve ...

Proper method of retrieving a property as a designated type within a union type

I have a scenario where I need to return a specific property from a function in various parts of an application. This property can fall into one of two categories, each with string literal properties. One category is an extension of the other. How can I ...

Exploring the power of Node JS with Promise.all and forEach within a nested array

When working on a Node app with typescript, I encountered the need to iterate through an array while calling an asynchronous function inside the loop to fetch information about related items for each item in the array. The function is called for each relat ...

I'm experiencing some strange symbols on my page that look like ''. It appears to be a problem occurring between the servlet and the Javascript. How can I resolve this issue?

After retrieving a CSV file from my servlet, I noticed that characters like 'é', 'á' or 'õ' are not displaying properly on my page. Strangely, when I access the servlet directly via browser, everything appears fine. I atte ...

tslint: no use of namespace and module is permitted

I have recently acquired a legacy .ts file that I would like to update. Two warnings appear: 'namespace' and 'module' are disallowed and The internal 'module' syntax is deprecated, use the 'namespace' keyword ins ...

Upon concatenation, the screen automatically returns to the beginning of the page

I've set up a page with a list of items, and at the bottom there's a handy "Load more" button that fetches new items to add on top of the existing list: handleLoadProducts = (append = false) => { this.setState({ isLoading: true, ...

Creating Dynamic Height for Div Based on Another Element's Height Using ReactJS and CSS

I'm attempting to set a fixed height for a div in order to enable overflow scrolling. However, I am encountering issues as I am using JavaScript within a useEffect hook to accomplish this task. The problem is inconsistent as sometimes the height is se ...

In Javascript, async functions automatically halt all ongoing "threads" when a new function begins

I have a dilemma with multiple async functions that can be called by the user at any point in time. It is crucial for me to ensure that all previously executed functions (and any potential "threads" they may have initiated) are terminated when a new functi ...

Are we on the correct path for breaking down code using react JS?

As I work on creating reusable table components, each column comes with its own set of functionalities, which results in dealing with numerous components. To streamline the process, I have been separating every component piece into individual files and ex ...

Exploring the Wonder of MVC with Ajax Calls Handling Multiple Parameters

I've been struggling to send 2 parameters from the View to the controller using an ajax call. Everything was working fine when I had only one parameter, but as soon as I added another, the function stopped working. Below is the Javascript code with a ...

Unleash full rotation capabilities in OrbitControls.js

Currently, I am utilizing OrbitControls.js to rotate the camera around a fixed point. My intention is not to restrict any vertical rotation - my goal is for the camera to smoothly circle the model (orbits around the pivot point). I experimented with remov ...

Launching a bootstrap modal within another modal

I am facing a minor issue with two modal popups on my website. The first modal is for the sign-in form and the second one is for the forgot password form. Whenever someone clicks on the "forgot password" option, the current modal closes and the forgot pas ...

Using dynamic import to pull in the map from an npm package.json in JavaScript

Is there a way to set up path mapping for pure JavaScript imports? Currently, when we want to import npm packages in pure JS without using webpack, the syntax is different. Instead of just using import package from 'package-name', we have to use ...

How can I reload a form page in Angular 9 after making a POST request with HttpClient?

I'm currently working on an Angular 9 application and am facing an issue with refreshing the page after making a POST request using HttpClient. Below is my form and its corresponding component: <form #myForm="ngForm" (ngSubmit)="save(myForm)"> ...

Include the selected class in the relative URL

I am attempting to apply a highlight class to my selected category: My code looks like this : <div id="category"> <ul> <a href="category.php?c=electronic"> <li>Electronic</li> </a> ...