Reputation: 2938
Suppose I have 2 components:
Parent's template:
<div #scrollable style="overflow: scroll">
<ng-content></ng-content>
</div>
Use case:
<parent>
<child></child>
</parent>
What would be an "angular", decoupled way of listening to scroll
event of #scrollable
div, but inside <child></child>
component?
AFAIK @HostListener
won't be able to target #scrollable
.
Thanks
Upvotes: 5
Views: 9501
Reputation: 144
In my opinion, below solution is the simplest / robust / comply with all conditions. Since, addEventListener is plain js.
const scroller = document.querySelector(".wrapper-element");
scroller?.addEventListener("scroll", (event) => {
if(scroller?.scrollTop > 300){//You can define<300> as per requirement.
this.windowScrolled = true;
} else{
this.windowScrolled = false;
}
});
Now using this, you are aware of either the element scrolled or not. This element could exist anywhere in the DOM, no restrictions of parent / child.
And you can also decide your scrolling volume. One down scroll = value 100.
Upvotes: 1
Reputation: 8269
You can actually listen to the scroll event through a Directive's HostListener
Had created a Stackblitz Demo for your reference. Check the console section for the console logs emitted from the example.
AppDirective
@Directive({
selector: '[listenScroll]'
})
export class AppDirective {
// This will emit the scroll update to whatever you want to assign with its emitted value
@Output() windowScroll: EventEmitter<any> = new EventEmitter<any>();
@HostListener("window:scroll", []) // Listens to Window Scroll Event
onWindowScroll() {
this.windowScroll.next('scrolled'); // Use the @Output() windowScroll to emit a simple string 'scrolled' whenever the window triggers a scroll event from the user
}
}
ParentComponent
@Component({
selector: 'my-app',
template: `
<div listenScroll // Our directive attached to the element
(windowScroll)="isScrolled$.next('scrolled')"> // Since the directive has been attached, we can also call and use its windowScroll @Output event emitted from the directive itself that we had created.
<child [scrollUpdate]="isScrolled$"></child> // Add an input [] scrollUpdate bracket to the ChildComponent to subscribe to whatever scroll event that its parent listens and passes to
</div>
`,
styles: [`div { height: 200vh; }`]
})
export class AppComponent {
isScrolled$: Subject<string> = new Subject<string>(); We use a RxJS Subject as it is a continuous event that we want to subscribe to dynamically
}
ChildComponent
@Component({
selector: 'child',
template: `<div>I am a child</div>`
})
export class ChildComponent implements OnInit {
@Input() scrollUpdate: string; // It stores here whatever the scroll event update that its parent has passed in to
constructor() {}
ngOnInit(): void {
// Listens and Subscribes to the latest emitted value from @Input() which its value is dependent from the parent's directive scroll update
this.scrollUpdate.subscribe(val => console.log('Child: ', val));
}
}
Upvotes: 3
Reputation:
Implement an interface that your child will implement :
interface ScrollableParent {
parentOnScroll(event: MouseScrollEvent);
}
Then use a template variable on your child selector to trigger the function from the parent template :
<parent (scroll)="child.parentOnScroll($event)">
<child #child></child>
</parent>
Upvotes: 3