"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$);
}
评论中讨论的另一个选项是 更新视图模型时:
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) {}
}
请注意,您可能想尝试 运行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 后 - 它成功了!
尝试在 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$);
}
评论中讨论的另一个选项是
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) {}
}
请注意,您可能想尝试 运行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 后 - 它成功了!