尝试使用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() { }
}
但该组件仅呈现初始状态并忽略流更新。 如果您打开控制台,在窗口调整大小时,您将看到来自同一个流的更新。
无法理解我在这里缺少的东西。
这是plunker
答案 0 :(得分: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
};
}
虽然我不想在这个模块中使用类/服务,只是清除/平台无关的构造,但这是唯一一种适用于角度的干净方式,无需关心触发区域更新。
答案 1 :(得分:10)
事件处理程序在Angular区域外运行,因此在事件触发时不会运行Angular更改检测。将事件处理程序放在组件中,然后它将与所有其他异步事件一起进行猴子修补,因此Angular更改检测将在每个事件之后执行(并更新视图):
ngOnInit() {
Observable.fromEvent(window, 'resize')
.map(getWindowSize)
.subscribe(windowSize$);
}
评论中讨论的另一个选项是在更新视图模型时manually run change detection:
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) {}
}
请注意,您可能希望尝试运行ApplicationRef.tick()
一次,例如在根组件中运行,该组件将对所有组件运行更改检测 - 而不是在每个组件中运行ChangeDetectorRef.detectChanges()
。 (并且您可能需要在tick()
方法中包含setTimeout()
,以确保所有组件视图模型都已更新...我不确定所有do()
回调的时间方法将被执行 - 即,如果它们都在JavaScript VM的一个回合中运行,或者涉及多个回合。)