Использование самого Router
себя вызовет проблемы, которые вы не можете полностью преодолеть, чтобы поддерживать постоянную работу браузера. На мой взгляд, лучший способ - просто использовать кастом directive
и позволить этому сбросить прокрутку по клику. Хорошая вещь об этом, то, что, если вы находитесь на том же самом, url
что вы нажимаете, страница также будет прокручиваться вверх. Это соответствует нормальным веб-сайтам. Основное directive
может выглядеть примерно так:
import {Directive, HostListener} from '@angular/core';
@Directive({
selector: '[linkToTop]'
})
export class LinkToTopDirective {
@HostListener('click')
onClick(): void {
window.scrollTo(0, 0);
}
}
Со следующим использованием:
<a routerLink="/" linkToTop></a>
Этого будет достаточно для большинства вариантов использования, но я могу представить несколько проблем, которые могут возникнуть из этого:
- Не работает
universal
из-за использованияwindow
- Небольшое влияние скорости на обнаружение изменений, потому что оно срабатывает при каждом нажатии
- Нет способа отключить эту директиву
На самом деле это довольно легко преодолеть эти проблемы:
@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)
);
}
}
}
Это учитывает большинство сценариев использования, при том же использовании, что и основной, с преимуществом включения / выключения:
<a routerLink="/" linkToTop></a> <!-- always active -->
<a routerLink="/" [linkToTop]="isActive"> <!-- active when `isActive` is true -->
рекламные ролики, не читайте, если вы не хотите, чтобы вас рекламировали
Еще одно улучшение может быть сделано, чтобы проверить, поддерживает ли браузер passive
события. Это немного усложнит код и будет немного неясным, если вы захотите реализовать все это в своих пользовательских директивах / шаблонах. Вот почему я написал небольшую библиотеку, которую вы можете использовать для решения этих проблем. Чтобы иметь ту же функциональность, что и выше, и с добавленным passive
событием, вы можете изменить свою директиву на эту, если вы используете ng-event-options
библиотеку. Логика внутри click.pnb
слушателя:
@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' })