Angular - Changing Input() from child component doesn't notify the parent that it was updated

3.2k Views Asked by At

I have a parent component which is passing some data (selectedId) to the child component using @Input().

Parent Template

<child-component [selectedId]="selectedId"></child-component>

The child component is using the selectedId as follows:

Child Template

<select [(ngModel)]="selectedId">
  <option [value]="option.id" *ngFor="let option of options">{{option.name}}</option>
</select>

Child Component

export class HelloComponent  {
  @Input()
    set selectedId(id: any) {
      this._selectedId = id;
  }
  get selectedId() {
    return this._selectedId;
  }

  public _selectedId;

  options = [
      {
        "id": "1",
        "name": "Weekly"
      },
      {
        "id": "2",
        "name": "Bi-Weekly"
      },
      {

        "id": "3",
        "name": "Lunar"
      },
      {
        "id": "4",
        "name": "Monthly"
      }
  ]
}

This works perfectly. The problem is when I change the selected item straight from the child component - not using the parent component.

This is the following scenario when it doesn't work as expected:

  1. The selectedId gets populated from a parent component (API call)
  2. The use manually changes select options (from child-component)
  3. API gets called again, and it should set the same ID as it set on step 1. How ever this doesn't trigger the change event. My assumption is that parent component isn't aware of the manual change done in a child-component, therefore it still holds a reference to the selectedId from step 1, and doesn't trigger the change in step 3 because it's the same ID.

I recreated the problem on Stackblitz

I am aware that I can use Output() and shared services to solve this, but is there any other way that I could inform the parent about that change detection?

2

There are 2 best solutions below

0
On

Approach 1: add this variable to a shared service and use it from there. It will get updated whenever child or parent modifies it.

Approach 2: pass an input from parent to child and add an event emitter so whenever values changes in child , you should emit an event to update value in parent component

0
On

you can use @Output to notify the parent comonent when there is change in the child component something like this


@Component({
  selector: 'hello',
  templateUrl: './hello.component.html',
  styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent  {
  @Input()
    set selectedId(id: any) {
      this._selectedId = id;
      console.log('Child change event --> ', id);
  }
  get selectedId() {
    return this._selectedId;
  }

  @Output() selectedIdChange=new EventEmitter();

  public _selectedId;

  options = [
      {
        "id": "1",
        "name": "Weekly"
      },
      {
        "id": "2",
        "name": "Bi-Weekly"
      },
      {
        "id": "3",
        "name": "Lunar"
      },
      {
        "id": "4",
        "name": "Monthly"
      }
  ]
  onChangeSelectedId(){
    this.selectedIdChange.emit(this.selectedId);
  }
}

and set the value like this

<hello [(selectedId)]="selectedId"></hello>