跟踪滚动位置并通知其他组件

时间:2016-04-07 06:31:20

标签: angular typescript events scroll

是否有一种简单的方法来跟踪浏览器滚动位置并通知多个组件有关它的信息?

用例:在滚动时,我希望能够根据我的位置更改页面上各种元素的类。在以前的angular版本中,它有点可能通过一个插件(对于jQuery来说也是如此)。当然,可以选择编写裸JS来在应用程序启动时初始化它并发出一个事件,但这听起来很脏,事件发射对于这类事情来说非常昂贵。

我有什么选择?

更新(建议后):

以下是我的尝试:

我创建了一个组件:

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);
    }
}

在app的主要指令中添加了一个属性:

<priz-app track-scroll>

并将该组件添加为顶级组件中的提供者之一:

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]
})

仍然没有...

另一个更新:

track-scroll移至主模板的其中一个div元素:

<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>

现在应用程序加载了一个完全空的屏幕。 FUN FUN FUN ...

最终解决方案(对我有用)。

  1. 定义指令:
  2. 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. 在任何使用它的地方添加它作为指令:
    2. directives: [TrackScrollDirective]
      
      1. 将属性添加到我们要跟踪事件的每个元素:
      2. <div class="col-md-12" track-scroll>
        

4 个答案:

答案 0 :(得分:52)

我认为最简单的方法是每个感兴趣的组件都会监​​听滚动事件。

  @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()

提示:

bootstrap(MyComponent, [
    provide(PLATFORM_DIRECTIVES, {useValue: [TrackScrollDirective], multi:true})]);

使指令通用,而不将其添加到每个组件directive: [...]列表中。

答案 1 :(得分:4)

我不得不以不同的方式解决这个问题,因为我需要在窗口上观看几个滚动元素。我创建了一个指令来观察元素上的滚动位置:

@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 }
}

然后在包含需要此元素的scroll元素的任何组件上,我可以@ViewChild这样的指令:

@Component({
  selector: 'parent',
  template: `
    <div class="container" scroll>
      // *ngFor=""...
    </div>
  `
})
export class ParentComp implements AfterViewChecked {

  @ViewChild(ScrollDir) scroll: ScrollDir;

  ngAfterViewChecked() {
    this.scroll.reset()
  }
}

答案 2 :(得分:1)

我有一个类似的问题,当用户滚动页面时,我需要滚动一个div,并使用下面的代码解决了我的问题。 在要捕获滚动的组件上:

import { HostListener } from '@angular/core';

@ViewChild('curtain') divCurtain: ElementRef;

export class ComponentX {
    @HostListener('window:scroll', ['$event']) onScrollEvent($event) {
        console.log(window.pageYOffset);
        this.divCurtain.nativeElement.style.top = window.pageYOffset.toString().concat('px');
    }

    ngOnInit(): void { }
}

仅此,我没有创建任何指令或其他代码。 每次用户滚动页面时都会执行一次HostListener,我得到window.pageYOffset才能发送到我的div。

我希望这会有所帮助。

答案 3 :(得分:0)

在角度文档项目中查看ScrollService的源代码。

他们获得职位的方式是fromEvent(window, 'scroll')

然后,您可以在注入组件的全局服务中执行以下操作:

public readonly windowScroll$ = fromEvent(window, 'scroll').pipe(map(x => window.scrollY), startWith(0), distinctUntilChanged(), shareReplay(1));

startWith(0)是必需的,因为在实际滚动之前您可能没有滚动事件。您可以根据需要添加反跳。