Використання самого 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' })