web-dev-qa-db-ja.com

「非同期」パイプがストリームの更新をレンダリングしない

asyncパイプを使用して、angular 2コンポーネントのストリームを介してウィンドウサイズ変更時にウィンドウサイズをレンダリングしようとしています:

<h2>Size: {{size$ | async | json}}</h2>

const windowSize$ = new BehaviorSubject(getWindowSize());
Observable.fromEvent(window, 'resize')
  .map(getWindowSize)
  .subscribe(windowSize$);

function getWindowSize() {
  return {
    height: window.innerHeight,
    width: window.innerWidth
  };
}

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div>
      <h2>Size: {{size$ | async | json}}</h2>
    </div>
  `,
  directives: []
})
export class App {
  size$ = windowSize$.do(o => console.log('size:', o));
  constructor() {  }
}

ただし、コンポーネントは初期状態をレンダリングするだけで、ストリームの更新を無視します。コンソールを開くと、ウィンドウのサイズ変更時に、同じストリームからの更新が表示されます。

私がここで何が欠けているのか理解できません。

これが プランカー

イベントハンドラーはAngularゾーンの外で実行されているため、イベントの発生時にAngular変更検出は実行されません。イベントハンドラーをコンポーネント内に配置してから、他のすべての非同期イベントと一緒にモンキーパッチが適用されるため、Angular変更検出は各イベントの後に実行されます(そしてビューを更新します):

_ngOnInit() {
    Observable.fromEvent(window, 'resize')
     .map(getWindowSize)
     .subscribe(windowSize$);
}
_

Plunker


コメントで説明されている別のオプションは、ビューモデルが更新されたときに 変更検出を手動で実行 することです。

_import {Component, ChangeDetectorRef} from 'angular2/core'
...
export class App {
  size$ = windowSize$.do(o => {
     console.log('size:', o);
     // since the resize event was not registered while inside the Angular zone,
     // we need to manually run change detection so that the view will update
     this._cdr.detectChanges();
  });

  constructor(private _cdr: ChangeDetectorRef) {}
}
_

Plunker

代わりに、たとえばルートコンポーネントでApplicationRef.tick()を1回実行してみると、各コンポーネントでChangeDetectorRef.detectChanges()を実行するのではなく、すべてのコンポーネントで変更検出が実行されることに注意してください。 (そして、すべてのコンポーネントビューモデルが更新されたことを確認するために、tick()setTimeout()メソッド内にラップする必要があるかもしれません...すべてのdo()コールバックメソッドが実行されます。つまり、すべてがJavaScript VMの1ターンで実行される場合、または複数のターンが含まれる場合です。)

11
Mark Rajcok

私の目標は別のモジュールでウィンドウサイズのストリームを抽象化できるようにすることだったので、どうやらストリームをクラスでラップするだけで取引が成立したようです。

「これは未来です」バージョン:

import {Observable, BehaviorSubject} from 'rxjs';  

export class WindowSize {
  width$: Observable<number>;
  height$: Observable<number>;

  constructor() {
    let windowSize$ = createWindowSize$();
    this.width$ = (windowSize$.pluck('width') as Observable<number>).distinctUntilChanged();
    this.height$ = (windowSize$.pluck('height') as Observable<number>).distinctUntilChanged();
  }
}

const createWindowSize$ = () =>
  Observable.fromEvent(window, 'resize')
    .map(getWindowSize)
    .startWith(getWindowSize())
    .publishReplay(1)
    .refCount();

const getWindowSize = () => {
  return {
    height: window.innerHeight,
    width: window.innerWidth
  }
};

「おばあちゃん」バージョン:

import {Observable, BehaviorSubject} from 'rxjs';

export class WindowSize {
    width$: Observable<number>;
    height$: Observable<number>;

    constructor() {
        let windowSize$ = new BehaviorSubject(getWindowSize());
        this.width$ = (windowSize$.pluck('width') as Observable<number>).distinctUntilChanged();
        this.height$ = (windowSize$.pluck('height') as Observable<number>).distinctUntilChanged();

        Observable.fromEvent(window, 'resize')
            .map(getWindowSize)
            .subscribe(windowSize$);
    }
}

function getWindowSize() {
    return {
        height: window.innerHeight,
        width: window.innerWidth
    };
}

このモジュールではクラス/サービスは必要ありませんでしたが、クリア/プラットフォームに依存しない構造だけでしたが、これは、ゾーンの更新のトリガーを気にすることなく、angularで機能する唯一のクリーンな方法でした。

14
Birowsky