Korzystanie z Routersamego 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 directivei 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 directivemoż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
universalpowodu 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 passivezdarzenia. 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 passivezdarzeniem, możesz zmienić dyrektywę na to, jeśli korzystasz z ng-event-optionsbiblioteki. Logika znajduje się wewnątrz click.pnbdetektora:
@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' })