Existe-t-il un moyen simple de suivre la position de défilement du navigateur et d’en informer plus d’un composant?
Cas d'utilisation: sur le défilement, je souhaite pouvoir modifier les classes de divers éléments de la page en fonction de l'endroit où je me trouve. Dans une version précédente de angular, c'était un peu possible grâce à un plugin (idem pour jQuery). Bien sûr, il est possible d'écrire JS nu pour l'initialiser au démarrage de l'application et émettre un événement, mais cela semble sale et l’émission d’événements est assez chère pour ce genre de chose.
Quelles sont mes options ici?
MISE À JOUR (après suggestions):
Voici ce que j'ai essayé:
J'ai créé un composant:
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);
}
}
ajouté un attribut à la directive principale d'une application:
<priz-app track-scroll>
et ajouté le composant en tant qu'un des fournisseurs dans le composant supérieur:
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]
})
Toujours rien...
Une autre mise à jour:
Déplacé track-scroll
à l’un des éléments div du modèle principal:
<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>
Et maintenant, l'application se charge avec un écran complètement vide. FUN FUN FUN ...
SOLUTION FINALE (qui a fonctionné pour moi).
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);
}
}
directives: [TrackScrollDirective]
<div class="col-md-12" track-scroll>
Je pense que le moyen le plus simple est que chaque composant intéressé écoute l’événement de défilement.
@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 utilisant @HostListener()
Allusion:
bootstrap(MyComponent, [
provide(PLATFORM_DIRECTIVES, {useValue: [TrackScrollDirective], multi:true})]);
rend la directive universelle sans l'ajouter à la liste de tous les composants directive: [...]
.
J'ai été obligé de résoudre ce problème différemment car j'avais besoin de regarder plusieurs éléments défiler sur la fenêtre. J'ai créé une directive pour surveiller la position du défilement sur un élément:
@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 }
}
Puis sur n'importe quel composant contenant un élément scroll qui avait besoin de cet élément, je pouvais @ViewChild
la directive comme celle-ci:
@Component({
selector: 'parent',
template: `
<div class="container" scroll>
// *ngFor=""...
</div>
`
})
export class ParentComp implements AfterViewChecked {
@ViewChild(ScrollDir) scroll: ScrollDir;
ngAfterViewChecked() {
this.scroll.reset()
}
}
Regardez la source de ScrollService , dans le cadre du projet de documentation angular).
La façon dont ils obtiennent la position est fromEvent(window, 'scroll')
Vous pouvez ensuite faire quelque chose comme ceci dans un service global que vous injectez dans votre composant:
public readonly windowScroll$ = fromEvent(window, 'scroll').pipe(map(x => window.scrollY), startWith(0), distinctUntilChanged(), shareReplay(1));
La startWith(0)
est nécessaire car un événement de défilement peut ne pas se produire avant que le défilement ne soit réellement effectué. Vous pouvez ajouter un rebond si nécessaire.