Korzystanie z Router
samego siebie spowoduje problemy, których nie można całkowicie rozwiązać, aby utrzymać spójne działanie przeglądarki. Moim zdaniem najlepszą metodą jest po prostu użycie niestandardowego directive
i pozwolenie na zresetowanie przewijania po kliknięciu. Dobrą rzeczą jest to, że jeśli jesteś na tym samym url
, co klikasz, strona również przewinie się do góry. Jest to zgodne ze zwykłymi stronami internetowymi. Podstawowy directive
może wyglądać mniej więcej tak:
import {Directive, HostListener} from '@angular/core';
@Directive({
selector: '[linkToTop]'
})
export class LinkToTopDirective {
@HostListener('click')
onClick(): void {
window.scrollTo(0, 0);
}
}
Przy następującym zastosowaniu:
<a routerLink="/" linkToTop></a>
To wystarczy w większości przypadków użycia, ale mogę sobie wyobrazić kilka problemów, które mogą z tego wyniknąć:
- Nie działa z
universal
powodu użyciawindow
- Mały wpływ na wykrywanie zmian ma szybkość, ponieważ jest uruchamiany przy każdym kliknięciu
- Nie ma możliwości wyłączenia tej dyrektywy
Naprawdę dość łatwo jest rozwiązać te problemy:
@Directive({
selector: '[linkToTop]'
})
export class LinkToTopDirective implements OnInit, OnDestroy {
@Input()
set linkToTop(active: string | boolean) {
this.active = typeof active === 'string' ? active.length === 0 : active;
}
private active: boolean = true;
private onClick: EventListener = (event: MouseEvent) => {
if (this.active) {
window.scrollTo(0, 0);
}
};
constructor(@Inject(PLATFORM_ID) private readonly platformId: Object,
private readonly elementRef: ElementRef,
private readonly ngZone: NgZone
) {}
ngOnDestroy(): void {
if (isPlatformBrowser(this.platformId)) {
this.elementRef.nativeElement.removeEventListener('click', this.onClick, false);
}
}
ngOnInit(): void {
if (isPlatformBrowser(this.platformId)) {
this.ngZone.runOutsideAngular(() =>
this.elementRef.nativeElement.addEventListener('click', this.onClick, false)
);
}
}
}
Uwzględnia to większość przypadków użycia, przy takim samym użyciu jak podstawowy, z tą zaletą, że włącza / wyłącza:
<a routerLink="/" linkToTop></a> <!-- always active -->
<a routerLink="/" [linkToTop]="isActive"> <!-- active when `isActive` is true -->
reklamy, nie czytaj, jeśli nie chcesz być reklamowany
Można wprowadzić kolejne ulepszenie, aby sprawdzić, czy przeglądarka obsługuje passive
zdarzenia. To skomplikuje kod jeszcze bardziej i jest nieco niejasne, jeśli chcesz zaimplementować je wszystkie w swoich niestandardowych dyrektywach / szablonach. Dlatego napisałem małą bibliotekę, której można użyć do rozwiązania tych problemów. Aby mieć taką samą funkcjonalność jak powyżej oraz z dodanym passive
zdarzeniem, możesz zmienić dyrektywę na to, jeśli korzystasz z ng-event-options
biblioteki. Logika znajduje się wewnątrz click.pnb
detektora:
@Directive({
selector: '[linkToTop]'
})
export class LinkToTopDirective {
@Input()
set linkToTop(active: string|boolean) {
this.active = typeof active === 'string' ? active.length === 0 : active;
}
private active: boolean = true;
@HostListener('click.pnb')
onClick(): void {
if (this.active) {
window.scrollTo(0, 0);
}
}
}
RouterModule.forRoot(appRoutes, { scrollPositionRestoration: 'enabled' })