【问题标题】:Setter is called twice with two-way binding in Angular在 Angular 中使用双向绑定调用 Setter 两次
【发布时间】:2021-12-19 08:50:55
【问题描述】:

在Angular中使用two-way binding时,似乎调用了两次子组件的setter。

Here is a playground 证明了这个问题。如果单击“从组件切换”按钮,则会调用两次 toggler.component.tsisShown 设置器。我复制了下面有趣的代码:

父组件

@Component({changeDetection: ChangeDetectionStrategy.OnPush})
export class AppComponent implements DoCheck {
  public isShown = true;

  public onToggle() {
    this.isShown = !this.isShown;
  }
}

子组件

@Component({changeDetection: ChangeDetectionStrategy.OnPush})
export class TogglerComponent {

  public get isShown(): boolean {
    return this._isShown;
  }

  @Input()
  public set isShown(isShown: boolean) {
    console.log('Entering setter component');
    this._isShown = isShown;
    this.isShownChange.emit(isShown);
  }

  @Output()
  public isShownChange: EventEmitter<boolean> = new EventEmitter();

  private _isShown: boolean = true;

  public onToggle() {
    this.isShown = !this.isShown;
  }
}

如何防止 setter 被调用两次?当父组件异步初始化绑定变量时,这种行为是有问题的。

【问题讨论】:

    标签: angular data-binding


    【解决方案1】:

    如果您不介意使用 rxjs 和流而不是 getter/setter,请查看此内容

    import { Component, ChangeDetectionStrategy, DoCheck } from '@angular/core';
    import { BehaviorSubject } from 'rxjs';
    import { shareReplay } from 'rxjs/operators';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css'],
      changeDetection: ChangeDetectionStrategy.OnPush,
    })
    export class AppComponent implements DoCheck {
      private readonly showSubject = new BehaviorSubject<boolean>(false);
    
      public readonly show$ = this.showSubject.asObservable().pipe(shareReplay());
    
      public toggle() {
        this.showSubject.next(!this.showSubject.value);
        console.log(
          `%c Outside: isShown changed to: ${this.showSubject.value}`,
          'color: blue;'
        );
      }
    
      public ngDoCheck() {
        console.log(
          `%c Outside: DoCheck: isShown: ${this.showSubject.value}`,
          'color: blue;'
        );
      }
    }
    
    
    <div>Outer isShown value: {{ show$ | async }}</div>
    <button (click)="toggle()">Toggle from outside</button>
    
    <br />
    <br />
    
    <toggler [show]="show$ | async" (showChange)="toggle()"></toggler>
    
    import {
      Component,
      ChangeDetectionStrategy,
      OnChanges,
      SimpleChanges,
      Input,
      Output,
      EventEmitter,
      DoCheck,
    } from '@angular/core';
    
    @Component({
      selector: 'toggler',
      template: `
        <div>
          <div>show: {{ show }}</div>
    
          <button (click)="toggle()">Toggle from component</button>
        </div>
      `,
      changeDetection: ChangeDetectionStrategy.OnPush,
      styles: [
        `
        :host {
          display: block;
          border: 1px dashed black;
          padding: 1em;
          color: green;
        }
      `,
      ],
    })
    export class TogglerComponent implements OnChanges, DoCheck {
      @Input() show?: boolean;
    
      @Output() showChange: EventEmitter<void> = new EventEmitter();
    
      public ngOnChanges(changes: SimpleChanges) {
        if (changes.show) {
          console.log(
            `%c OnChanges: isShown changed to: ${this.show}`,
            'color: green;'
          );
        }
      }
    
      public ngDoCheck() {
        console.log(`%c DoCheck: isShown: ${this.show}`, 'color: green;');
      }
    
      public toggle() {
        this.showChange.emit();
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-19
      • 1970-01-01
      • 2016-01-29
      • 2020-09-12
      • 2018-12-07
      • 1970-01-01
      • 1970-01-01
      • 2021-06-10
      相关资源
      最近更新 更多