Runtime Terror
Runtime Terror

Reputation: 6742

The same boolean value in @Input(), doesn't trigger change detection

I have an angular app, where I have a popup component. I can control the popups visibility through it's parent and also from itself.

app.component.ts

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

@Component({
  selector: 'my-app',
  template: `
  <popup [visible]="visible"></popup>
  <button (click)="onShow()">Show</button>
  <button (click)="onHide()">Hide</button>
  `,
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  public visible: boolean = false;
  public onShow(): void {
    this.visible = true;
  }
  public onHide(): void {
    this.visible = false;
  }
}

popup.component.ts

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

@Component({
  selector: 'popup',
  template: `<div *ngIf="visible">My fancy popup <button (click)="onClick()">Click to close</button></div>`,
})
export class PopupComponent  {
  @Input()
  public visible: boolean;

  public onClick(): void {
    this.visible = false;
  }
}

Working stackblitz.

Use case:

The problem:

As far as I know, its because the visible member inside app.component.ts doesn't changes, so the PopupComponent's @Input() value doesn't changes either. Any idea how to fix this?

Upvotes: 2

Views: 5285

Answers (3)

Piyush Jain
Piyush Jain

Reputation: 1986

popup.component.ts

import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'popup',
  template: `<div *ngIf="visible">My fancy popup <button (click)="onClick()">Click to close</button></div>`,
})
export class PopupComponent implements OnInit  {
  @Input() visible;
  @Output() visibleUpdate = new EventEmitter();
  // public visible: boolean;

  ngOnInit() {
    console.log('ngOnInit', this.visible);
  }

  public onClick(): void {
    this.visible= false;
    this.visibleUpdate.emit(this.visible);

    console.log('onClick', this.visible);
  }
}

app.component.ts

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

@Component({
  selector: 'my-app',
  template: `<popup [visible]="visible" (visibleUpdate)="visibleUpdated($event)"></popup>
    <button (click)="onShow()">Show</button>
    <button (click)="onHide()">Hide</button>`,
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  public visible: boolean = false;

  public onShow(): void {
    this.visible = true;

    console.log('onShow', this.visible);
  }
  public onHide(): void {
    this.visible = false;

    console.log('onHide', this.visible);
  }

  public visibleUpdated($event): void {
    this.visible = $event;

    console.log('visibleUpdate', $event)
  }
}

Let me know if you have any doubt.

Upvotes: 1

Saurabh Yadav
Saurabh Yadav

Reputation: 3386

You can use Event Emitter to pass value from child to parent

APP component

import { Component, Input, Output, EventEmitter } from '@angular/core';

        @Component({
          selector: 'popup',
          template: `<div *ngIf="visible">My fancy popup <button (click)="onClick()">Click to close</button></div>`,
        })
        export class PopupComponent  {
          @Input()
          public visible: boolean;
          @Output() close: EventEmitter<any> = new EventEmitter();

          public onClick(): void {
            this.visible = false;
            this.toggle();
          }
          toggle() {
             this.close.emit(false);
          }
        }

App Component

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

@Component({
  selector: 'my-app',
  template: `
  <popup [visible]="visible" (close)="onHide($event)"></popup>
  <button (click)="onShow()">Show</button>
  <button (click)="onHide()">Hide</button>
  `,
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  public visible: boolean = false;
  public onShow(): void {
    this.visible = true;
  }
  public onHide(value: boolean): void {
    console.log(value)
    if(value) {
      this.visible = value;
    } else {
          this.visible = false;
    }

  }

}

Upvotes: 1

Lia
Lia

Reputation: 11982

You Should emit changes from child to parent:

export class PopupComponent  {
  @Input()
  public visible: boolean;
  @Output() visibleChange: EventEmitter<boolean> = new EventEmitter<boolean>();

  public onClick(): void {
    this.visible = false;
    this.visibleChange.emit(this.visible);
  }
}

parent:

@Component({
  selector: 'my-app',
  template: `
  <popup [(visible)]="visible"></popup>
  <button (click)="onShow()">Show</button>
  <button (click)="onHide()">Hide</button>
  `,
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  public visible: boolean = false;
  public onShow(): void {
    this.visible = true;
  }
  public onHide(): void {
    this.visible = false;
  }
}

forked demo

Upvotes: 5

Related Questions