"async" 管道未呈现流更新

"async" pipe not rendering the stream updates

尝试在 window 上渲染 window 大小,通过 angular 2 组件中的流调整大小使用 async 管道:

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

但是组件只渲染初始状态而忽略流更新。 如果您打开控制台,在 window 调整大小时,您会看到来自同一流的更新。

不明白我在这里错过了什么。

这是一个plunker

事件处理程序 运行 在 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

请注意,您可能想尝试 运行ning ApplicationRef.tick() 一次,比如在您的根组件中,这将 运行 更改对所有组件的检测 – 而不是 运行ning ChangeDetectorRef.detectChanges() 在每个组件中。 (并且您可能需要将 tick() 包装在 setTimeout() 方法中,以确保更新所有组件视图模型......我不确定所有 do() 回调何时方法将被执行——即,如果它们全部 运行 在 JavaScript VM 的一个回合中,或者如果涉及多个回合。)

因为我的目标是能够在不同的模块中抽象出 window 大小的流,显然只是将流包装在 class 中就达成了协议:

"This is the future"版本:

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

"Granny"版本:

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

虽然我不想在这个模块中使用 class/service,只需要 clear/platform-independent 构造,这是唯一适用于 angular 且无需关心触发区域更新的干净方法.

@Marks 解决方案对我没有用,有用的是下面的 hack:

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
     setTimeout(()=>this._cdr.detectChanges())
});  

但后来我真的想到了 Mark 的注释“事件处理程序 运行 在 Angular 区域之外”,并意识到在我的用例中我使用的是 javascript 的 firestore而不是 angular 的 firestore,它在 diffrenet 事件处理程序上观察快照更改。切换回 angular 的 firestore 后 - 它成功了!