16

async尝试通过使用管道的角度 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() {  }
}

但是组件只呈现初始状态并忽略流更新。如果您打开控制台,在调整窗口大小时,您将看到来自同一流的更新。

无法理解我在这里缺少什么。

这是一个笨蛋

4

3 回答 3

14

由于我的目标是能够在不同的模块中抽象窗口大小的流,显然只是将流包装在一个类中就可以了:

“这就是未来”版本:

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 且无需关心触发区域更新的干净方式。

于 2016-02-20T13:50:28.850 回答
14

事件处理程序在 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()一次,例如在您的根组件中,它将在所有组件上运行更改检测 - 而不是ChangeDetectorRef.detectChanges()在每个组件中运行。(并且您可能需要包装tick()在一个setTimeout()方法中,以确保所有组件视图模型都已更新......我不确定何时do()会执行所有回调方法 - 即,如果它们都在一个回合中运行JavaScript VM,或者如果涉及多个轮次。)

于 2016-02-19T19:26:24.887 回答
0

@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 后——它起作用了!

于 2021-09-13T15:24:04.223 回答