【问题标题】:The same boolean value in @Input(), doesn't trigger change detection@Input() 中的相同布尔值不会触发更改检测
【发布时间】:2019-11-08 08:38:33
【问题描述】:

我有一个 Angular 应用程序,其中有一个弹出组件。我可以通过它的父级以及它自身来控制弹出窗口的可见性。

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

工作stackblitz

用例:

  • 用户可以通过单击弹出窗口中的按钮来关闭弹出窗口
  • 用户可以通过单击父组件中的按钮来关闭弹出窗口
  • 用户可以通过单击父组件中的按钮来显示弹出窗口

问题:

  • 点击显示弹出窗口
  • 在弹出窗口中单击以隐藏它
  • 点击显示弹出窗口
  • 弹出窗口不显示

据我所知,这是因为app.component.ts 中的visible 成员没有改变,所以PopupComponent@Input() 值也没有改变。知道如何解决这个问题吗?

【问题讨论】:

  • 您可以添加一个事件发射器,该事件发射器使用 @Output 装饰器通知关闭事件的父组件,并在两者中都将可见更改回 false,否则我建议将 viibility 标志存储在获取的服务中注入到两个组件中。

标签: angular


【解决方案1】:

您应该从子级向父级发出更改:

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

父母:

@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

【讨论】:

  • 虽然这确实有效,但看起来非常冗长。我认为每个任务都应该触发 changeDetection ?
【解决方案2】:

您可以使用Event Emitter 将值从子级传递给父级

APP组件

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

应用组件

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

  }

}

【讨论】:

    【解决方案3】:

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

    如果您有任何疑问,请告诉我。

    【讨论】:

      猜你喜欢
      • 2020-05-16
      • 2019-05-07
      • 1970-01-01
      • 2021-01-23
      • 2017-06-19
      • 2019-03-20
      • 2021-02-09
      • 1970-01-01
      • 2011-04-25
      相关资源
      最近更新 更多