1

我有组件树,每个组件都有changeDetection: ChangeDetectionStrategy.OnPush,在这种情况下,祖先组件不会检测到它们正在使用的本地更改,例如 observables 或setTimeout(). 我通过this.ref.markForCheck()在应该更新组件的位置添加调用来做到这一点。但我想这可能不是解决问题的好方法。

那么什么是真正的方法呢?如果我使用第三方组件并且它没有this.ref.markForCheck()调用它可能不起作用,我是对的吗?

这是我的最低级别组件的代码:

import {
  Component, HostBinding, ViewEncapsulation, AfterContentInit,
  Input, ViewChild, ElementRef, ChangeDetectionStrategy, ChangeDetectorRef,
} from '@angular/core';
import { Observable, Subscription } from 'rxjs';
import * as $ from 'jquery';

import { DestroyUnsubscribe } from 'services';

@Component({
  selector: 'jf-more',
  encapsulation: ViewEncapsulation.None,
  // changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrls: ['more.component.scss'],
  template: `
    <div class="more__open-button" *ngIf="!opened" (click)="onToggleClick()">
      <ng-content select="jf-more-open"></ng-content>
    </div>
    <div class="more__text" [ngClass]="{'more__text_opened': opened}" #text>
      <ng-content></ng-content>
    </div>
    <div class="more__close-button" *ngIf="opened" (click)="onToggleClick()">
      <ng-content select="jf-more-close"></ng-content>
    </div>
  `,
})
@DestroyUnsubscribe()
export class MoreComponent implements AfterContentInit {
  @Input() oneLineHeight = 16;
  @ViewChild('text') text: ElementRef;
  @HostBinding('class.more') mainClass = true;
  @HostBinding('class.more_initiated') initiated: boolean = false;
  @HostBinding('class.more_one-line') oneLine: boolean;
  opened: boolean;

  private subscriptions: Subscription[] = [];

  constructor(private ref: ChangeDetectorRef) {}

  ngAfterContentInit() {
    this.makeLineCheck();
    this.subscriptions.push(
      Observable
        .fromEvent(window, 'resize')
        .debounceTime(500)
        .subscribe(() => this.makeLineCheck())
    );
  }

  onToggleClick() {
    this.opened = !this.opened;
    // this.ref.detectChanges();
    // this.ref.markForCheck();
  }

  private makeLineCheck() {
    this.initiated = false;
    this.opened = true;
    this.oneLine = false;
    // this.ref.detectChanges();
    this.ref.markForCheck();
    console.log('makeLineCheck', Object.assign({}, this));
    setTimeout(() => {
      this.oneLine = ($(this.text.nativeElement).innerHeight() <= this.oneLineHeight);
      this.initiated = true;
      this.opened = false;
      // this.ref.detectChanges();
      this.ref.markForCheck();
      console.log('setTimeout makeLineCheck', Object.assign({}, this));
    });
  }
}
4

1 回答 1

2

ApplicationRef.tick()setTimeout(...)为根节点调用更改检测,然后向下传播。如果两者之间的组件设置为OnPush并且没有输入更改,那么这不会导致这些组件中的更改检测。

我会使用带有 observables 的共享服务来通知祖先有关更改,以便他们自己调用markForCheck()

于 2016-12-02T18:20:29.183 回答