Angular2 Enrutamiento con Hashtag para anclaje de página

Deseo agregar algunos enlaces en mi página Angular2, que al hacer clic, saltará a posiciones específicas dentro de esa página, como lo hacen los hashtags normales. Entonces los enlaces serían algo así como

/users/123#userInfo /users/123#userPhoto /users/123#userLikes 

etc.

No creo que necesite HashLocationStrategy, ya que estoy bien con la forma normal de Angular2, pero si lo agrego directamente, el enlace realmente saltará a la raíz, no en algún lugar en la misma página. Cualquier dirección es apreciada, gracias.

Actualizar

Esto ahora es compatible

 Jump to 'Test' anchor  
 this._router.navigate( ['/somepath', id ], {fragment: 'test'}); 

Agregue el código siguiente a su componente para desplazarse

  import {ActivatedRoute} from '@angular/router'; // < -- do not forget to import private fragment: string; constructor(private route: ActivatedRoute) { } ngOnInit() { this.route.fragment.subscribe(fragment => { this.fragment = fragment; }); } ngAfterViewInit(): void { try { document.querySelector('#' + this.fragment).scrollIntoView(); } catch (e) { } } 

Original

Este es un problema conocido y se rastrea en https://github.com/angular/angular/issues/6595

Aunque la respuesta de Günter es correcta, no cubre el “salto a” la parte de la etiqueta de anclaje .

Por lo tanto, adicionalmente a:

 Jump to 'Test' anchor  
 this._router.navigate( ['/somepath', id ], {fragment: 'test'}); 

… en el componente (padre) donde necesita un comportamiento de “saltar a”, agregue:

 import { Router, NavigationEnd } from '@angular/router'; class MyAppComponent { constructor(router: Router) { router.events.subscribe(s => { if (s instanceof NavigationEnd) { const tree = router.parseUrl(router.url); if (tree.fragment) { const element = document.querySelector("#" + tree.fragment); if (element) { element.scrollIntoView(true); } } } }); } } 

Tenga en cuenta que esto es una solución ! Sigue este problema de github para futuras actualizaciones. ¡Créditos a Victor Savkin por proporcionar la solución!

Un poco tarde, pero aquí hay una respuesta que encontré que funciona:

 Anchor 

Y en el componente:

 constructor( private route: ActivatedRoute, private router: Router ) {} onAnchorClick ( ) { this.route.fragment.subscribe ( f => { const element = document.querySelector ( "#" + f ) if ( element ) element.scrollIntoView ( element ) }); } 

Lo anterior no se desplaza automáticamente a la vista si aterriza en una página con un ancla, entonces usé la solución anterior en mi ngInit para que también funcione con eso:

 ngOnInit() { this.router.events.subscribe(s => { if (s instanceof NavigationEnd) { const tree = this.router.parseUrl(this.router.url); if (tree.fragment) { const element = document.querySelector("#" + tree.fragment); if (element) { element.scrollIntoView(element); } } } }); } 

Asegúrese de importar Router, ActivatedRoute y NavigationEnd al comienzo de su componente y debería estar listo para continuar.

Fuente

Ninguna de las respuestas anteriores funcionó para mí. En un último esfuerzo, lo intenté en mi plantilla:

 From Here 
To Here

Con esto en mi .ts:

 onClick(){ let x = document.querySelector("#foobar"); if (x){ x.scrollIntoView(); } } 

Y funciona como se esperaba para los enlaces internos. Esto en realidad no usa tags de anclaje por lo que no tocaría la URL en absoluto.

Las soluciones anteriores no funcionaron para mí … Esta lo hizo:

Primero, prepare MyAppComponent para el desplazamiento automático en ngAfterViewChecked ()

 import { Component, OnInit, AfterViewChecked } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Subscription } from 'rxjs'; @Component( { [...] } ) export class MyAppComponent implements OnInit, AfterViewChecked { private scrollExecuted: boolean = false; constructor( private activatedRoute: ActivatedRoute ) {} ngAfterViewChecked() { if ( !this.scrollExecuted ) { let routeFragmentSubscription: Subscription; // Automatic scroll routeFragmentSubscription = this.activatedRoute.fragment .subscribe( fragment => { if ( fragment ) { let element = document.getElementById( fragment ); if ( element ) { element.scrollIntoView(); this.scrollExecuted = true; // Free resources setTimeout( () => { console.log( 'routeFragmentSubscription unsubscribe' ); routeFragmentSubscription.unsubscribe(); }, 1000 ); } } } ); } } } 

Luego, navegue a my-app-route enviando el hashtag de prodID

 import { Component } from '@angular/core'; import { Router } from '@angular/router'; @Component( { [...] } ) export class MyOtherComponent { constructor( private router: Router ) {} gotoHashtag( prodID: string ) { this.router.navigate( [ '/my-app-route' ], { fragment: prodID } ); } } 

Añadiendo a la respuesta de Kalyoyan, esta suscripción está vinculada al enrutador y continuará hasta que la página se actualice por completo. Cuando se suscriba a eventos de enrutador en un componente, asegúrese de darse de baja en ngOnDestroy:

 import { OnDestroy } from '@angular/core'; import { Router, NavigationEnd } from '@angular/router'; import { Subscription } from "rxjs/Rx"; class MyAppComponent implements OnDestroy { private subscription: Subscription; constructor(router: Router) { this.subscription = router.events.subscribe(s => { if (s instanceof NavigationEnd) { const tree = router.parseUrl(router.url); if (tree.fragment) { const element = document.querySelector("#" + tree.fragment); if (element) { element.scrollIntoView(element); } } } }); } public ngOnDestroy() { this.subscription.unsubscribe(); } } 

Después de leer todas las soluciones, busqué un componente y encontré uno que hace exactamente lo que la pregunta original me pidió: desplazamiento a enlaces de anclaje. https://www.npmjs.com/package/ng2-scroll-to

Cuando lo instalas, usas syntax como:

 // app.awesome.component.ts @Component({ ... template: `... Scroll to main section  

Me ha funcionado muy bien.

Dado que la propiedad del fragmento aún no proporciona desplazamiento de anclaje, esta solución alternativa me sirvió de algo:

  

Acabo de hacer esto trabajando en mi propio sitio web, así que pensé que valdría la pena publicar mi solución aquí.

 Link Text!  
They're trying to anchor to me!

Y luego en su componente, asegúrese de incluir esto:

  import { ActivatedRoute } from '@angular/router'; constructor(private route: ActivatedRoute) { this.route.fragment.subscribe ( f => { const element = document.querySelector ( "#" + f ) if ( element ) element.scrollIntoView ( element ) }); } 

Una solución simple que funciona para páginas sin ningún parámetro de consulta, es el navegador hacia atrás / adelante, el enrutador y el deep-linking cumple.

 Go To Anchor 1 ngOnInit() { // If your page is dynamic this.yourService.getWhatever() .then( data => { this.componentData = data; setTimeout(() => this.jumpToId( window.location.hash.substr(1) ), 100); } ); // If your page is static // this.jumpToId( window.location.hash.substr(1) ) } jumpToId( fragment ) { // Use the browser to navigate window.location.hash = fragment; // But also scroll when routing / deep-linking to dynamic page // or re-clicking same anchor if (fragment) { const element = document.querySelector('#' + fragment); if (element) element.scrollIntoView(); } } 

El tiempo de espera es simplemente permitir que la página cargue cualquier dato dynamic “protegido” por un * ngIf. Esto también se puede usar para desplazarse a la parte superior de la página al cambiar de ruta, solo proporcione una etiqueta de anclaje superior predeterminada.

Todas las demás respuestas funcionarán en la versión angular <6.1. Pero si tienes la última versión, entonces no necesitarás hacer estos feos hacks ya que Angular ha resuelto el problema.

aquí está el enlace para emitir

Todo lo que tendría que hacer es configurar scrollOffset con la opción del segundo argumento del método RouterModule.forRoot .

 @NgModule({ imports: [ RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled', anchorScrolling: 'enabled', scrollOffset: [0, 64] // [x, y] }) ], exports: [RouterModule] }) export class AppRoutingModule {} 

Aquí hay otra solución con referencia a la respuesta de JavierFuentes:

 Jump to Element 

en script:

 import {ActivatedRoute} from "@angular/router"; import {Subscription} from "rxjs/Subscription"; export class Links { private scrollExecuted: boolean = false; constructor(private route: ActivatedRoute) {} ngAfterViewChecked() { if (!this.scrollExecuted) { let routeFragmentSubscription: Subscription; routeFragmentSubscription = this.route.fragment.subscribe(fragment => { if (fragment) { let element = document.getElementById(fragment); if (element) { element.scrollIntoView(); this.scrollExecuted = true; // Free resources setTimeout( () => { console.log('routeFragmentSubscription unsubscribe'); routeFragmentSubscription.unsubscribe(); }, 0); } } }); } } gotoHashtag(fragment: string) { const element = document.querySelector("#" + fragment); if (element) element.scrollIntoView(element); } } 

Esto permite al usuario desplazarse directamente al elemento, si el usuario aterriza directamente en la página que tiene el hashtag en la url.

Pero en este caso, he suscrito el Fragment de ruta en ngAfterViewChecked pero se llama continuamente a ngAfterViewChecked() por cada ngDoCheck y no permite al usuario desplazarse hacia atrás, por lo que se routeFragmentSubscription.unsubscribe después de un tiempo de espera de 0 milis después de la vista. desplazado al elemento.

Además, el método gotoHashtag se define para desplazarse a un elemento cuando el usuario hace clic específicamente en la etiqueta de anclaje.

Actualizar:

Si url tiene [routerLink]="['self-route', id]" , [routerLink]="['self-route', id]" anchor no conservará las cadenas de consulta. Intenté seguir una solución para lo mismo:

 Jump to Element constructor( private route: ActivatedRoute, private _router:Router) { } ... ... gotoHashtag(fragment: string) { let url = ''; let urlWithSegments = this._router.url.split('#'); if(urlWithSegments.length){ url = urlWithSegments[0]; } window.location.hash = fragment; const element = document.querySelector("#" + fragment); if (element) element.scrollIntoView(element); } 

Acabo de probar el complemento muy útil disponible en nmp – ngx-scroll-to , que funciona muy bien para mí. Sin embargo, está diseñado para Angular 4+, pero tal vez alguien encuentre útil esta respuesta.

Probé la mayoría de estas soluciones pero tuve problemas para irme y regresar con otro fragmento, pero no funcionó, así que hice algo un tanto diferente que funciona al 100% y me deshice del hash feo en la URL.

tl; dr aquí hay una mejor manera de lo que he visto hasta ahora.

 import { Component, OnInit, AfterViewChecked, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Subscription } from 'rxjs/Subscription'; @Component({ selector: 'app-hero', templateUrl: './hero.component.html', styleUrls: ['./hero.component.scss'] }) export class HeroComponent implements OnInit, AfterViewChecked, OnDestroy { private fragment: string; fragSub: Subscription; constructor(private route: ActivatedRoute) { } ngOnInit() { this.fragSub = this.route.fragment.subscribe( fragment => { this.fragment = fragment; }) } ngAfterViewChecked(): void { try { document.querySelector('#' + this.fragment).scrollIntoView({behavior: 'smooth'}); window.location.hash = ""; } catch (e) { } } ngOnDestroy() { this.fragSub.unsubscribe(); } } 

Tuve el mismo problema. La solución: utilizando View port Scroller https://angular.io/api/common/ViewportScroller#scrolltoanchor

  lens  

– Código del componente:

 export class ParametrageComponent { constructor(private viewScroller: ViewportScroller) {} scrollTo(tag : string) { this.viewScroller.scrollToAnchor(tag); } } 

¡Este trabaja para mí! Esta ngPara que ancle dinámicamente la etiqueta, debe esperar a que se visualice

HTML:

 
{{cm.namae}} Reply Blah Blah

Mi archivo ts:

 private fragment: string; @ViewChildren('ngForComments') AnchorComments: QueryList; ngOnInit() { this.route.fragment.subscribe(fragment => { this.fragment = fragment; }); } ngAfterViewInit() { this.AnchorComments.changes.subscribe(t => { this.ngForRendred(); }) } ngForRendred() { this.jumpToId() } jumpToId() { let x = document.querySelector("#" + this.fragment); console.log(x) if (x){ x.scrollIntoView(); } } 

¡No olvides importar ViewChildren , QueryList , etc. y agregar algún constructor ActivatedRoute !