简体   繁体   中英

Dependent drop-down list with Angular reactive forms

I'm having a strange issue with Angular Reactive Forms with a dependent drop-down list, which I'm sure is a result of my lack of knowledge.

I'd like to have the options in the second drop-down dependent on how the first drop-down is set. When the first drop-down changes, set the array of options for the second drop-down, and choose the first element in the array.

Here is a StackBlitz which I think demonstrates the issue

If I choose "SAT" in the first drop-down, I see the expected "Verbal" in the second drop-down, but if I then choose "ACT", the second drop-down appears empty, though I think the form field value is actually "English" which is what I expect.

Here is my html in the StackBlitz.

<p>
  Start editing to see some magic happen :)
</p>
<form [formGroup]="testForm" id="testForm">
  <select id="testType" formControlName="testType" (change)="changeTestType()">
    <option *ngFor="let tType of testTypes">{{tType}}</option>
  </select>
  <select id="testName" formControlName="testName">
    <option *ngFor="let tName of currentTestNames">{{tName}}</option>
  </select>
</form>

Here is my typescript file for this component.

import { Component, VERSION, OnInit } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  constructor(private fb: FormBuilder) {}

  testTypes: string[] = ["ACT", "SAT"];

  testNames: Object = {
    ACT: ["English", "Math", "Composite"],
    SAT: ["Verbal", "Math", "Composite", "Evidence Based Read/Write"]
  };

  currentTestType = this.testTypes[0];
  currentTestNames = this.testNames[this.currentTestType];

  testForm = this.fb.group({
    testType: this.currentTestType,
    testName: this.currentTestNames[0]
  });

  // Does nothing for now.
  ngOnInit() {}

  changeTestType() {
    let newTestType = this.testForm.get("testType").value;

    if (newTestType != this.currentTestType) {
      this.currentTestType = newTestType;
      this.currentTestNames = this.testNames[this.currentTestType];

      // Set test name to be the first thing in the testNames array for this test type.
      this.testForm.patchValue({
        testName: this.currentTestNames[0]
      });
    }
  }
}

Thanks for any suggestions. Maybe I'm using the wrong lifecycle event to try to do this logic or have some other issue.

UPDATE:

I notice this seems to work much better if I use arrays of objects or "any" type which contain multiple properties, and use [ngValue] in the options, with it set to the entire object, as has probably been recommended elsewhere. I'd like to also try using ngValue but with arrays of strings.

New StackBlitz link

Update 2: Yes, Here's another test using ngValue and an arrays of strings which also seems to be working better than my original test which had options with no ngValue set where the value was set between the option tags using Angular interpolation syntax like <option>{{tType}}</option> .

StackBlitz link

@ji102 Even I don't know what actually happens. But I found one thing If the ACT typenames length has beyond 3 like below one.

Like

`ACT: ["English", "Math", "Composite", "Component"]`

instead of

`ACT: ["English", "Math", "Composite"]`

Ref: https://stackblitz.com/edit/angular-ivy-ogzxer?file=src%2Fapp%2Fapp.component.ts

It works as you expect.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM