Tracking scroll position and notifying other components about it
I think the easiest way is each interested component listening to the scroll event.
@Component({
...
// alternative to `@HostListener(...)`
// host: {'(window:scroll)': 'doSomething($event)'}
})
class SomeComponent {
@HostListener('window:scroll', ['$event'])
doSomething(event) {
// console.debug("Scroll Event", document.body.scrollTop);
// see András Szepesházi's comment below
console.debug("Scroll Event", window.pageYOffset );
}
}
plunker
Plunker using @HostListener()
Hint:
bootstrap(MyComponent, [
provide(PLATFORM_DIRECTIVES, {useValue: [TrackScrollDirective], multi:true})]);
makes the directive universal without adding it to every components directive: [...]
list.
I was forced to solve this differently because I needed to watch several scrolling elements on the window. I created a directive to watch the scroll position on an element:
@Directive({
selector: '[scroll]'
})
export class ScrollDir {
@Output() setScroll = new EventEmitter();
private scroll: number;
constructor(private el: ElementRef) { }
@HostListener('scroll', ['$event'])
scrollIt() { this.scroll = event.srcElement.scrollTop }
reset() { this.el.nativeElement.scrollTop = this.scroll }
}
Then on any any component containing a scroll element that needed this element I could @ViewChild
the directive like this:
@Component({
selector: 'parent',
template: `
<div class="container" scroll>
// *ngFor=""...
</div>
`
})
export class ParentComp implements AfterViewChecked {
@ViewChild(ScrollDir) scroll: ScrollDir;
ngAfterViewChecked() {
this.scroll.reset()
}
}
Look at the source to ScrollService, as part of the angular documentation project.
The way they get the position is fromEvent(window, 'scroll')
You can then do something like this in a global service you inject into your component:
public readonly windowScroll$ = fromEvent(window, 'scroll').pipe(map(x => window.scrollY), startWith(0), distinctUntilChanged(), shareReplay(1));
The startWith(0)
is needed because you may not get a scroll event until you actually scroll. You can add debouncing if needed.