Aleksandr Makov
Aleksandr Makov

Reputation: 2938

Angular: listen scroll event on parent's component?

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

Answers (3)

MAQ
MAQ

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

KShewengger
KShewengger

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

user4676340
user4676340

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

Related Questions