2

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:

  • user can close the popup by clicking on button in popup
  • user can close the popup by clicking on button in parent's component
  • user can display the popup by clicking on button in parent's component

The problem:

  • click to show the popup
  • click inside popup to hide it
  • click to show the popup
  • the popup doesn't show up

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?

1
  • 1
    you could add an event emitter which informs the parent component of the closing event with the @Output decorator and change visible back to false in both, otherwise I would suggest storing the vibility flag in a service which gets injected into both components. Commented Nov 8, 2019 at 8:48

3 Answers 3

5

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

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

1 Comment

although this does work, it seems very verbose. i think every assignment shoud trigger changeDetection 🤔
1

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;
    }

  }

}

Comments

1

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.

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.