Vishal Lengare
Vishal Lengare

Reputation: 83

angular dynamic dropdown change event doesn’t trigger automatically

So I came with issue related to angular dropdowns, I have multiple dropdowns where they are dependent on last dropdowns response. In simple word suppose I have Class - Subject - Part like these tree structure. when I select the Class then the all Subjects inside the Class will appear. but here when there is only on Subject is there in Class then it is get automatically selected and I cant even interact with that option and the next dropdown is depends on these Subject dropdowns value. How to tackle these issue in angular??

<form [formGroup]="addPartsForm" (ngSubmit)="addPart()">
            <div class="card-body">
              <div class="row">
                <div class="col-md-6">
                  <div class="control">
                    <label for="board_id">Select Board</label>
                    <select name="board_id" id="board_id" formControlName="board_id" class="form-control" (change)="onBoardSelectionChange($event, addPartsForm)">
                      <option [ngValue]="null" disabled selected>Select board</option>
                      <option *ngFor="let board of boardsAllData" [value]="board.id">{{board.name}}</option>
                    </select>
                    <div class="errorMsg" *ngIf="addPartsForm.get('board_id')?.touched && addPartsForm.get('board_id')?.invalid">
                      <p class="error">Please select a board</p>
                    </div>
                  </div>
                </div>
                <div class="col-md-6">
                  <div class="control">
                    <label for="class_id">Select Class</label>
                    <select name="class_id" id="class_id" formControlName="class_id" class="form-control" (change)="onClassSelectionChange($event, addPartsForm)">
                      <option [ngValue]="null" >Select class</option>
                      <option *ngFor="let class of classAllData" [value]="class.id">{{class.name}}</option>
                    </select>
                    <div class="errorMsg" *ngIf="addPartsForm.get('class_id')?.touched && addPartsForm.get('class_id')?.invalid">
                      <p class="error">Please select a class</p>
                    </div>
                  </div>
                </div>
                <div class="col-md-6 mt-2">
                  <div class="control">
                    <label for="subject_id">Select Subject</label>
                    <select name="subject_id" id="subject_id" formControlName="subject_id" class="form-control">
                      <option [ngValue]="null" >Select subject</option>
                      <option *ngFor="let subject of subjectAllData" [value]="subject.id">{{subject.name}}</option>
                    </select>
                    <div class="errorMsg" *ngIf="addPartsForm.get('subject_id')?.touched && addPartsForm.get('subject_id')?.invalid">
                      <p class="error">Please select a subject</p>
                    </div>
                  </div>
                </div>
                <div class="col-md-6 mt-2">
                  <div class="control">
                    <label for="partsName">Enter Part Name</label>
                    <input type="text" name="partsName" id="partsName" formControlName="part_name" class="form-control" placeholder="Enter part name">
                  </div>
                </div>
                <div class="col-md-6 mt-3">
                  <div class="control">
                    <label for="description">Enter Part Description</label>
                    <input type="text" name="description" id="description" formControlName="description" class="form-control" placeholder="Enter part description">
                  </div>
                </div>
              </div>
            </div>
            <div class="card-footer">
              <button class="button" mat-flat-button type="button" (click)="addPartsForm.reset()">Clear</button> &nbsp; &nbsp;
              <button class="button" mat-flat-button [disabled]="!addPartsForm.valid">Add Part</button>
            </div>
          </form>

Upvotes: 1

Views: 25

Answers (1)

Karthik Senniyappan
Karthik Senniyappan

Reputation: 115

I had a scenario in Angular with multiple dependent dropdowns:

  • Board → Class → Subject → Part
  • When there’s only one subject after selecting a class, it should auto-select but still allow user interaction.
  • The next dropdown depends on this selection.

app.component.html

<form [formGroup]="addPartsForm" (ngSubmit)="addPart()">
<div class="card-body">
  <div class="row">
    <div class="col-md-6">
      <label>Select Board</label>
      <select formControlName="board_id" class="form-control" (change)="onBoardSelectionChange($event)">
        <option [ngValue]="null" disabled selected>Select board</option>
        <option *ngFor="let board of boardsAllData" [value]="board.id">{{ board.name }}</option>
      </select>
    </div>

    <div class="col-md-6">
      <label>Select Class</label>
      <select formControlName="class_id" class="form-control" (change)="onClassSelectionChange($event)">
        <option [ngValue]="null">Select class</option>
        <option *ngFor="let class of classAllData" [value]="class.id">{{ class.name }}</option>
      </select>
    </div>

    <div class="col-md-6 mt-2">
      <label>Select Subject</label>
      <select formControlName="subject_id" class="form-control" (change)="onSubjectSelectionChange($event)">
        <option [ngValue]="null">Select subject</option>
        <option *ngFor="let subject of subjectAllData" [value]="subject.id">{{ subject.name }}</option>
      </select>
    </div>

    <div class="col-md-6 mt-2">
      <label>Enter Part Name</label>
      <input type="text" formControlName="part_name" class="form-control" placeholder="Enter part name" />
    </div>

    <div class="col-md-6 mt-3">
      <label>Enter Part Description</label>
      <input type="text" formControlName="description" class="form-control" placeholder="Enter part description" />
    </div>
  </div>
</div>

<div class="card-footer">
  <button type="button" class="btn btn-secondary" (click)="addPartsForm.reset()">Clear</button>
  <button class="btn btn-primary" type="submit" [disabled]="!addPartsForm.valid">Add Part</button>
</div>
</form>

app.component.ts

addPartsForm!: FormGroup;
  boardsAllData: any[] = [];
  classAllData: any[] = [];
  subjectAllData: any[] = [];
  partsAllData: any[] = [];

  constructor(private fb: FormBuilder) {}

  ngOnInit(): void {
    this.initializeForm();
    this.loadBoards();
  }

  initializeForm() {
    this.addPartsForm = this.fb.group({
      board_id: [null, Validators.required],
      class_id: [null, Validators.required],
      subject_id: [null, Validators.required],
      part_name: ['', Validators.required],
      description: [''],
    });
  }

  loadBoards() {
    this.boardsAllData = [
      { id: 'b1', name: 'CBSE' },
      { id: 'b2', name: 'ICSE' },
    ];
  }

  onBoardSelectionChange(event: any) {
    const boardId = event.target.value;
    if (boardId) {
      const classAllData = [
        { id: 'c1', name: 'Class 10', board_id: 'b1' },
        { id: 'c2', name: 'Class 12', board_id: 'b2' },
      ];
      this.classAllData = classAllData.filter((x) => x.board_id == boardId);
      this.addPartsForm.patchValue({ class_id: null, subject_id: null });
      this.subjectAllData = [];
      this.partsAllData = [];
      if (this.classAllData.length === 1) {
        // If only one subject, select it but still keep the dropdown enabled
        this.addPartsForm.get('class_id')?.setValue(this.classAllData[0].id);
        this.onClassSelectionChange({
          target: { value: this.classAllData[0].id },
        });
      } else {
        // Reset if multiple subjects
        this.addPartsForm.get('class_id')?.setValue(null);
      }
    }
  }

  onClassSelectionChange(event: any) {
    const classId = event.target.value;
    if (classId) {
      const subjectAllData = [
        { id: 's1', name: 'Mathematics', class_id: 'c1' },
        { id: 's2', name: 'Science', class_id: 'c1' },
        { id: 's3', name: 'Physics', class_id: 'c2' },
      ];
      const data = subjectAllData.filter((x) => x.class_id == classId);
      this.subjectAllData = data;
      if (data.length === 1) {
        this.addPartsForm.patchValue({ subject_id: data[0].id });
        this.addPartsForm.updateValueAndValidity();
        this.loadParts(data[0].id);
      } else {
        this.addPartsForm.patchValue({ subject_id: null });
        this.partsAllData = [];
      }
    }
  }

  onSubjectSelectionChange(event: any) {
    const subjectId = event.target.value;
    if (subjectId) this.loadParts(subjectId);
  }

  loadParts(subjectId: string) {
    this.partsAllData = [
      { id: 'p1', name: 'Algebra', subject_id: 's1' },
      { id: 'p2', name: 'Geometry', subject_id: 's1' },
      { id: 'p3', name: 'Thermodynamics', subject_id: 's3' },
    ];
  }

  addPart() {
    if (this.addPartsForm.valid) {
    }
  }

Sample mock json

{
  "boardsAllData": [
    { "id": "b1", "name": "CBSE" },
    { "id": "b2", "name": "ICSE" }
  ],
  "classAllData": [
    { "id": "c1", "name": "Class 10", "board_id": "b1" },
    { "id": "c2", "name": "Class 12", "board_id": "b1" },
    { "id": "c3", "name": "Class 10", "board_id": "b2" }
  ],
  "subjectAllData": [
    { "id": "s1", "name": "Mathematics", "class_id": "c1" },
    { "id": "s2", "name": "Science", "class_id": "c1" },
    { "id": "s3", "name": "Physics", "class_id": "c2" }
  ],
  "partsAllData": [
    { "id": "p1", "name": "Algebra", "subject_id": "s1" },
    { "id": "p2", "name": "Geometry", "subject_id": "s1" },
    { "id": "p3", "name": "Thermodynamics", "subject_id": "s3" }
  ]
}

Upvotes: 0

Related Questions