0
votes

I'm relatively new to Angular. I was trying to pass data from parent component to child component with the use of @Input(), but the changes are not reflected. I have a parent component with a select element, I want to pass the value of the selected item to the children. The parent component has a mat-tab-group with a few mat-tab(s).

Parent HTML

<mat-form-field>
    <mat-select placeholder="Class" [formControl]="selectedClassControl" (selectionChange)="changeClassId()">
        <mat-option *ngFor="let class of classes" value="{{class.id}}">{{class.classNumber}}-{{class.section}}</mat-option>
    </mat-select>
</mat-form-field>
<mat-tab-group color="primary" (selectedIndexChange)="selectTab($event)">
    <mat-tab label="Units">
        <app-units [classId]="classId" *ngIf="selectedTab === 0"></app-units>
    </mat-tab>
    <!-- many such tabs with the same structure --> 
</mat-tab-group>

Parent TS

  classId : number = 0;
  classes: Class[];
  selectedClass: any;
  selectedClassControl = new FormControl(this.selectedClass);
  constructor(private classService: ClassService, private stateMgmtService: 
StateManagementService) { 
    var schoolId = this.stateMgmtService.getSchoolId();
    this.classService.getClasses(schoolId).subscribe(data =>{
      this.classes = data;
    })
  }
  selectedTab = 0;
  ngOnInit(): void {
  }

  selectTab(event) {
    this.selectedTab = event;
  }

  changeClassId(){
    this.classId = this.selectedClassControl.value;
    console.log("ClassId changed to "+this.classId);
  }

Child HTML

<mat-form-field>
    <mat-select placeholder="Subject" [formControl]="selectedSubjectControl" >
        <mat-option *ngFor="let subject of subjects" value="{{subject.id}}">{{subject.code}}-{{subject.name}}</mat-option>
    </mat-select>
</mat-form-field>

Child TS


  @Input() classId : number = 0;
  subjects: Subject[];
  selectedSubject: any;
  selectedSubjectControl = new FormControl(this.selectedSubject);
  constructor(private classService: ClassService) { 
    console.log("classId"+this.classId);
    if(this.classId != 0)
    classService.getSubjects(this.classId).subscribe( data => {
      this.subjects = data;
    })
  }

What I see is that when I change my selection in the parent component, the classId is not communicated to the child component, should I manually refresh the component for the changes to get reflected?
Any help much appreciated...
TIA

2

2 Answers

3
votes

Couple of things here. First be careful of using Subject as a class name Subject is an object in rxjs that you might accidentally be importing. I would make your class name more specific.

Next you really don't need the selectionChange event logic. Instead you can subscribe to the valueChanged property of the form control and set the classId variable whenever it changes like this

this.selectedClassControl.valueChanges.subscribe(e => this.classId = e),

next in your child component the classId will update but your Subject array won't

I would instead use a setter on your input like this

@Input() set classId(val: number) {
   this._classId = val;
 if(val != 0) {
  classService.getSubjects(val).subscribe( data => {
   this.subjects = data;
  }); //assuming this is an http service so no unsubscribe is required
}
}
private _classId: number = 0;
1
votes

The constructor might be fired too soon for the input variable. You could try to manually listen to changes of the input variable using OnChanges hook. Try the following

import { Component, Input, OnChanges } from "@angular/core";

export class ChildComponent implements OnChanges {
  @Input() classId: number = 0;
  subjects: Subject[];
  selectedSubject: any;
  selectedSubjectControl = new FormControl(this.selectedSubject);

  constructor(private classService: ClassService) { }

  ngOnChanges() {
    console.log("classId" + this.classId);
    if(this.classId != 0) {
      this.classService.getSubjects(this.classId).subscribe( data => {
        this.subjects = data;
      });
    }
  }

  private doSomething(input: string) {}
}