"async"管道不呈现流更新

Bir*_*sky 13 rxjs rxjs5 angular2-changedetection angular

尝试在窗口上渲染窗口大小,通过使用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() {  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

这是一个吸烟者

Bir*_*sky 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
  }
};
Run Code Online (Sandbox Code Playgroud)

"奶奶"版本:

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
    };
}
Run Code Online (Sandbox Code Playgroud)

虽然我不想在这个模块中使用类/服务,只是清除/平台无关的构造,但这是唯一一种适用于角度的干净方式,无需关心触发区域更新.

  • @PankajParkar,如果服务不依赖于其他服务,则不需要`@Injectable()`装饰器(即使你把它放在`bootstrap()`数组中).所以我的问题仍然存在.我仍然想知道/询问这个用例是否有所不同.我的预感是这里不需要`@Injectable`.(但是,我知道最好总是包含`@Injectable`装饰器.) (2认同)
  • @MarkRajcok你应该把钱放在上面.它没有`@Injectable()` (2认同)

Mar*_*cok 11

事件处理程序在Angular区域外运行,因此在事件触发时不会运行Angular更改检测.将事件处理程序放在组件中,然后它将与所有其他异步事件一起进行猴子修补,因此Angular更改检测将在每个事件之后执行(并更新视图):

ngOnInit() {
    Observable.fromEvent(window, 'resize')
     .map(getWindowSize)
     .subscribe(windowSize$);
}
Run Code Online (Sandbox Code Playgroud)

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) {}
}
Run Code Online (Sandbox Code Playgroud)

Plunker

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

  • @Birowsky,一种方法是在进行视图模型更改后手动运行更改检测:http://stackoverflow.com/a/35106069/215945,这是一个显示:http://plnkr.co/edit/的plunker eiua3K9aRRFEyxV3vbtP?p =预览 (2认同)