El uso de Router
sí mismo causará problemas que no puede superar por completo para mantener una experiencia de navegador consistente. En mi opinión, el mejor método es simplemente usar un personalizado directive
y dejar que esto restablezca el desplazamiento al hacer clic. Lo bueno de esto es que si está en lo mismo url
que hace clic, la página también se desplazará hacia arriba. Esto es consistente con los sitios web normales. Lo básico directive
podría verse así:
import {Directive, HostListener} from '@angular/core';
@Directive({
selector: '[linkToTop]'
})
export class LinkToTopDirective {
@HostListener('click')
onClick(): void {
window.scrollTo(0, 0);
}
}
Con el siguiente uso:
<a routerLink="/" linkToTop></a>
Esto será suficiente para la mayoría de los casos de uso, pero puedo imaginar algunos problemas que pueden surgir de esto:
- No funciona
universal
debido al uso dewindow
- Impacto de poca velocidad en la detección de cambios, ya que se activa por cada clic
- No hay forma de deshabilitar esta directiva
En realidad, es bastante fácil superar estos problemas:
@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)
);
}
}
}
Esto tiene en cuenta la mayoría de los casos de uso, con el mismo uso que el básico, con la ventaja de habilitarlo / deshabilitarlo:
<a routerLink="/" linkToTop></a> <!-- always active -->
<a routerLink="/" [linkToTop]="isActive"> <!-- active when `isActive` is true -->
comerciales, no leas si no quieres que te anuncien
Se podría hacer otra mejora para verificar si el navegador admite o no passive
eventos. Esto complicará un poco más el código y es un poco oscuro si desea implementar todo esto en sus directivas / plantillas personalizadas. Es por eso que escribí una pequeña biblioteca que puede usar para abordar estos problemas. Para tener la misma funcionalidad que anteriormente, y con el passive
evento agregado , puede cambiar su directiva a esto, si usa la ng-event-options
biblioteca. La lógica está dentro del click.pnb
oyente:
@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' })