这是我的代码:
app.component.ts
notifier$ = new BehaviorSubject<any>({});
notify() {
this.notifier$.next({});
}
app.component.html
<div (scroll)="notify()"></div>
<child-component [inp]="notifier$ | async" />
问题在于,当用户滚动时,notify()
函数会被重复调用,并且我只想在用户每次滚动时一次调用notify()
。
我可以这样完成我想要的事情:
scrolling = false;
scrollingTimer: NodeJS.Timer;
notify() {
clearTimeout(this.scrollingTimer);
if (!this.scrolling) {
this.notifier$.next({});
}
this.scrolling = true;
this.scrollingTimer = setTimeout(() => (this.scrolling = false), 1000);
}
但我会希望使用rxjs
进行此操作。但是debounceTime
与我想要的相反,throttleTime
和auditTime
都不是我想要的。有办法吗?
答案 0 :(得分:2)
您可以像这样建立一个可观察的物体:
const scroll$ = fromEvent(document, 'scroll');
const scrollEnd$ = scroll$.pipe(
switchMapTo(
timer(1000) // on every scroll, restart a timer
)
);
const scrollStart$ = scrollEnd$.pipe( // the scroll end event triggers switch to scroll$
startWith(0), // but start it off
switchMapTo(
scroll$.pipe( // then just take the first scroll$ event
first()
)
)
);
scrollStart$.subscribe(v => console.log('scroll start'));
您可以将其概括为一个运算符:
function firstTimeout(timeout: number) { // welcoming notes on a better name
return input$ => {
const inputTimeout$ = input$.pipe(
switchMapTo(timer(timeout))
);
return inputTimeout$.pipe(
startWith(0),
switchMapTo(input$.pipe(first()))
);
};
}
并像这样使用它:
notifier$.pipe(firstTimeout(1000)).subscribe(v => console.log('took one'));
在这种情况下,一个好主意是将其包装在指令中以方便重用:
@Directive({
selector: '[scrollStart]'
})
export class ScrollStartDirective {
private scrollSource = new Subject();
@HostListener('scroll', ['$event'])
private onScroll(event) {
this.scrollSource.next(event);
}
@Output()
scrollStart = new EventEmitter();
constructor() {
this.scrollSource.pipe(firstTimeout(1000)).subscribe(this.scrollStart);
}
}
然后您可以像这样使用它:
<div (scrollStart)="notify()"></div>
答案 1 :(得分:0)
您可以使用take(1)
this.inp.pipe(
take(1),
).subscribe(res => console.log(res));
take(1)仅获取第一个值并完成。无需进一步的逻辑。
您当然应该在子组件中使用以上内容:)
此外,由于您的观察结果即将完成...您可以创建一个新主题,并在每次滚动时将其传递给子组件
notify() {
this.notifier$ = new BehaviorSubject<any>({});
this.notifier$.next({});
}
答案 2 :(得分:0)
用户滚动时,您希望notify$
针对每个滚动事件发出。这提供了恒定的发射值流。因此,您希望notifier$
在流开始时发出一次,在空闲1秒时再次发出。
notify$ = new Subject();
notifier$ = merge(
notify$.pipe(first()),
notify$.pipe(switchMap(value => of(value).pipe(delay(1000))))
).pipe(
take(2),
repeat()
);
<div (scroll)="notify$.next()"></div>
您合并了两个观察值。第一个立即发射,第二个在1秒的延迟后发射。您使用switchMap以便延迟的可观察项始终重新启动。
我们采用下两个触发流完成的值,并使用repeat来重新开始。