0

I have three component-- Component A, Component B and Component C. Component A wraps Component B. Component B wraps Component C. If I use @Input() in each component, will changes to options in the root component pass through the nested components to Component C? See below:

<component-a [options]="options">
  <component-b [options]="options">
    <component-c [options]="options">
    </component-c>
  </component-b>
</component-a>

I asked this question because when options changes in the root component of the application I need to do some filtering at each nested component. However, I have noticed some unusual behavior with a third-party component that doesn't seem to receive options? I can elaborate further after the first part of this question is answered.

I believe the answer to the question is "Yes". So here is the second part. How do I update <ng-select> when the components are wraps as follows:

<component-a [options]="options">
  <component-b [options]="options">
    <ng-select [items]="options" [(ngModel)]="selectedOption">
    </ng-select>
  </component-b>
</component-a>

When the documentation for <ng-select> says the following:

Change Detection

Ng-select component implements OnPush change detection which means the dirty checking checks for immutable data types. That means if you do object mutations like:

this.items.push({id: 1, name: 'New item'})

Component will not detect a change. Instead you need to do:

this.items = [...this.items, {id: 1, name: 'New item'}];

This will cause the component to detect the change and update. Some might have concerns that this is a pricey operation, however, it is much more performant than running ngDoCheck and constantly diffing the array.

So how do I ensure changes to options is reflected in <ng-select>?

3 Answers 3

2

I have a workaround. Since your options is a object , angular won't detect changes to the object. You need to change the reference of the object in order for angular to detech the changes

Below is the code that will work and you change detection will be as expected

this.options = Object.assign({},this.options)

The above code will change the reference of the object due to which change detection will be triggered

Sign up to request clarification or add additional context in comments.

Comments

0

Yes, you can pass @Input parameters down through multiple components in this way. I don't know the answer to your question about ng-select, however in the component.ts file you can use the ngOnChanges lifecycle hook to detect when an @Input value has changed. Here is a decent article about it: https://ngdev.space/angular-2-input-property-changes-detection-3ccbf7e366d2

Comments

0

Ultimately, I decided to use <ng-option> template and *ngFor although I do like @renil-babu way to mutate.

<ng-select [id]="name"
           [attr.name]="name"
           [placeholder]="placeholder">
    <ng-option *ngFor="let input of options" [value]="input.value">
        {{ input.text }}
    </ng-option>
</ng-select>

Update: Unfortunately, this will not work since the the variable that is passed down isn't honored when options change

The final implementation requires immutability and (add) event to trigger it.

<ng-select
  [items]="items"
  [multiple]="true"
  (add)="exclude($event, items)" 
  [(ngModel)]="selected">
</ng-select>
<br>
<ng-select
  [items]="items2"
  [multiple]="true" 
  (add)="exclude($event, items2)" 
  [(ngModel)]="selected2">
</ng-select>
<br>
<ng-select
  [items]="items3"
  [multiple]="true" 
  (add)="exclude($event, items3)" 
  [(ngModel)]="selected3">
</ng-select>

And here is the Typescript:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

  selected;
  selected2;
  selected3;

  items: string[];
  items2: string[];
  items3: string[];

  all :any[];

  ngOnInit() {
    this.items = ['Vilnius', 'Kaunas', 'Klaipeda'];
    this.items2 = [...this.items];
    this.items3 = [...this.items];

    this.all = [this.items, this.items2, this.items3];
  }

  exclude(item, currentArray) {
    for (let i = 0; i < 3; i++) {
      const items = this.all[i];
      if (items !== currentArray) {
        this.all[i] = items.filter(x => x !== item);
      }
    }
    this.items = this.all[0];
    this.items2 = this.all[1];
    this.items3 = this.all[2];
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.