Tracking scroll position and notifying other compo

2019-01-08 09:26发布

Is there an easy way to track the browser scroll position and notify more than a single component about it?

Use case: On scroll I want to be able to change classes of various elements on the page based upon where I am. In a previous version of angular it was somewhat possible through a plugin (same for jQuery). Of course, there is the option of writing bare JS to initialize it on application start and emit an event, but that sounds dirty, and event emission is pretty expensive for this type of thing.

What are my options here?


UPDATE (after suggestions):

Here is what I tried:

I created a component:

import {Component} from "angular2/core";

@Component({
    selector: '[track-scroll]',
    host: {'(window:scroll)': 'track($event)'},
    template: ''
})

export class TrackScrollComponent {
    track($event) {
        console.debug("Scroll Event", $event);
    }
}

added an attribute to the main directive of an app:

<priz-app track-scroll>

and added the component as one of the providers in the top component:

import {TrackScrollComponent} from "../../shared/components/track-scroll.component";

@Component({
  selector: 'priz-app',
  moduleId: module.id,
  templateUrl: './app.component.html',
  directives: [ROUTER_DIRECTIVES, SecureRouterOutlet, AppHeader, TrackScrollComponent],
  providers: [AuthenticationService]
})

Still nothing...


ANOTHER UPDATE:

Moved track-scroll to one of the div elements of the main template:

<div class="container-fluid" track-scroll>
    <div class="row">
        <div class="col-md-12">
            <app-header></app-header>
            <secure-outlet signin="Login" unauthorized="AccessDenied"></secure-outlet>
        </div>
    </div>
</div>

And now the app loads with a completely empty screen. FUN FUN FUN...


FINAL SOLUTION (that worked for me).

  1. Define a directive:
import {Directive} from "angular2/core";

@Directive({
    selector: '[track-scroll]',
    host: {'(window:scroll)': 'track($event)'}
})

export class TrackScrollDirective {
    track($event: Event) {
        console.debug("Scroll Event", $event);
    }
}
  1. Add it as a directive everywhere that uses it:
directives: [TrackScrollDirective]
  1. Add the attribute to each element we want to track the event:
<div class="col-md-12" track-scroll>

2条回答
祖国的老花朵
2楼-- · 2019-01-08 09:47

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()
  }
}
查看更多
霸刀☆藐视天下
3楼-- · 2019-01-08 09:58

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.

查看更多
登录 后发表回答