Desidero aggiungere alcuni collegamenti sulla mia pagina Angular2, che quando si fa clic, salterà a posizioni specifiche all'interno di quella pagina, come i normali hashtag. Quindi i collegamenti sarebbero qualcosa di simile
/users/123#userInfo
/users/123#userPhoto
/users/123#userLikes
eccetera.
Non penso di aver bisogno di HashLocationStrategy, dato che sto bene con il normale modo Angular2, ma se aggiungo direttamente, il link salterà effettivamente alla radice, non da qualche parte sulla stessa pagina. Qualsiasi direzione è apprezzata, grazie.
Update
Questo è ora supportato
<a [routerLink]="['somepath']" fragment="Test">Jump to 'Test' anchor </a>
this._router.navigate( ['/somepath', id ], {fragment: 'test'});
Aggiungi sotto il codice al tuo componente per scorrere
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) { }
}
Originale
Questo è un problema noto e tracciato a https://github.com/angular/angular/issues/6595
Sebbene la risposta di Günter sia corretta, non copre il "salto" alla parte del tag di ancoraggio .
Pertanto, in aggiunta a:
<a [routerLink]="['somepath']" fragment="Test">Jump to 'Test' anchor </a>
this._router.navigate( ['/somepath', id ], {fragment: 'test'});
... nel componente (genitore) in cui hai bisogno di un comportamento "salta a", aggiungi:
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); }
}
}
});
}
}
Si prega di notare che questa è una soluzione alternativa ! Segui questo problema github per gli aggiornamenti futuri. Crediti a Victor Savkin per aver fornito la soluzione!
Un po 'tardi ma ecco una risposta che ho trovato che funziona:
<a [routerLink]="['/path']" fragment="test" (click)="onAnchorClick()">Anchor</a>
E nel componente:
constructor( private route: ActivatedRoute, private router: Router ) {}
onAnchorClick ( ) {
this.route.fragment.subscribe ( f => {
const element = document.querySelector ( "#" + f )
if ( element ) element.scrollIntoView ( element )
});
}
Quanto sopra non scorre automaticamente alla vista se si atterra già su una pagina con un'ancora, quindi ho usato la soluzione sopra in my ngInit in modo che potesse funzionare anche con quella:
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); }
}
}
});
}
Assicurati di importare Router, ActivatedRoute e NavigationEnd all'inizio del tuo componente e dovrebbe essere tutto a posto.
Ci scusiamo per aver risposto un po 'tardi; C'è una funzione predefinita nella Angular Routing Documentation che ci aiuta nell'instradamento con un hashtag per l'ancoraggio della pagina i.e, anchorScrolling: 'enabled'
Step-1: - Prima importa il RouterModule nel file app.module.ts: -
imports:[
BrowserModule,
FormsModule,
RouterModule.forRoot(routes,{
anchorScrolling: 'enabled'
})
],
Step-2: - Vai alla pagina HTML, crea la navigazione e aggiungi due attributi importanti come [routerLink] e fragment per la corrispondenza dei rispettivi Div ID : -
<ul>
<li> <a [routerLink] = "['/']" fragment="home"> Home </a></li>
<li> <a [routerLink] = "['/']" fragment="about"> About Us </a></li>
<li> <a [routerLink] = "['/']" fragment="contact"> Contact Us </a></li>
</ul>
Step-3: - Crea una sezione/div facendo corrispondere il nome ID con il frammento : -
<section id="home" class="home-section">
<h2> HOME SECTION </h2>
</section>
<section id="about" class="about-section">
<h2> ABOUT US SECTION </h2>
</section>
<section id="contact" class="contact-section">
<h2> CONTACT US SECTION </h2>
</section>
Come riferimento, ho aggiunto l'esempio seguente creando una piccola demo che aiuta a risolvere il tuo problema.
Nessuna delle risposte precedenti ha funzionato per me. In un ultimo disperato tentativo, ho provato nel mio modello:
<a (click)="onClick()">From Here</a>
<div id='foobar'>To Here</div>
Con questo nel mio .ts:
onClick(){
let x = document.querySelector("#foobar");
if (x){
x.scrollIntoView();
}
}
E funziona come previsto per i collegamenti interni. Questo in realtà non utilizza i tag di ancoraggio in modo che non tocchi affatto l'URL.
Le soluzioni di cui sopra non hanno funzionato per me ... Questo l'ha fatto:
Innanzitutto, prepara MyAppComponent
per lo scorrimento automatico in 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 );
}
}
} );
}
}
}
Quindi, passa a my-app-route
inviando l'hashtag 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 } );
}
}
Utilizzare questo per il modulo router in app-routing.module.ts
:
@NgModule({
imports: [RouterModule.forRoot(routes, {
useHash: true,
scrollPositionRestoration: 'enabled',
anchorScrolling: 'enabled',
scrollOffset: [0, 64]
})],
exports: [RouterModule]
})
Questo sarà nel tuo codice HTML:
<a href="#/users/123#userInfo">
Tutte le altre risposte funzioneranno su Angular versione <6.1. Ma se hai l'ultima versione allora non avrai bisogno di fare questi brutti hack come Angular ha risolto il problema.
Tutto quello che devi fare è impostare scrollOffset
con l'opzione del secondo argomento del metodoRouterModule.forRoot
.
@NgModule({
imports: [
RouterModule.forRoot(routes, {
scrollPositionRestoration: 'enabled',
anchorScrolling: 'enabled',
scrollOffset: [0, 64] // [x, y]
})
],
exports: [RouterModule]
})
export class AppRoutingModule {}
Aggiungendo la risposta di Kalyoyan , questa sottoscrizione è legata al router e continuerà fino a quando la pagina non sarà completamente aggiornata. Quando ti abboni agli eventi del router in un componente, assicurati di annullare l'iscrizione in 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();
}
}
Ho appena ottenuto questo lavoro sul mio sito Web, quindi ho pensato che sarebbe valso la pena pubblicare la mia soluzione qui.
<a [routerLink]="baseUrlGoesHere" fragment="nameOfYourAnchorGoesHere">Link Text!</a>
<a name="nameOfYourAnchorGoesHere"></a>
<div>They're trying to anchor to me!</div>
E poi nel tuo componente, assicurati di includere questo:
import { ActivatedRoute } from '@angular/router';
constructor(private route: ActivatedRoute) {
this.route.fragment.subscribe ( f => {
const element = document.querySelector ( "#" + f )
if ( element ) element.scrollIntoView ( element )
});
}
Dopo aver letto tutte le soluzioni, ho cercato un componente e ne ho trovato uno che fa esattamente ciò che la domanda originale chiedeva: scorrere verso i collegamenti di ancoraggio. https://www.npmjs.com/package/ng2-scroll-to
Quando lo installi, usi la sintassi come:
// app.awesome.component.ts
@Component({
...
template: `...
<a scrollTo href="#main-section">Scroll to main section</a>
<button scrollTo scrollTargetSelector="#test-section">Scroll to test section</a>
<button scrollTo scrollableElementSelector="#container" scrollYTarget="0">Go top</a>
<!-- Further content here -->
<div id="container">
<section id="main-section">Bla bla bla</section>
<section id="test-section">Bla bla bla</section>
<div>
...`,
})
export class AwesomeComponent {
}
Ha funzionato davvero bene per me.
Poiché la proprietà del frammento non fornisce ancora lo scrolling dell'ancora, questa soluzione alternativa ha fatto il trucco per me:
<div [routerLink]="['somepath']" fragment="Test">
<a href="#Test">Jump to 'Test' anchor </a>
</div>
Una soluzione semplice che funziona per pagine senza parametri di query, è browser back/forward, router e deep-linking compliant.
<a (click)="jumpToId('anchor1')">Go To Anchor 1</a>
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();
}
}
Il timeout è semplicemente per consentire alla pagina di caricare qualsiasi dato dinamico "protetto" da un * ngIf. Questo può anche essere utilizzato per scorrere fino alla parte superiore della pagina quando si cambia rotta: è sufficiente fornire un tag di ancoraggio superiore predefinito.
Ecco un'altra soluzione con riferimento alla risposta JavierFuentes:
<a [routerLink]="['self-route', id]" fragment="some-element" (click)="gotoHashtag('some-element')">Jump to Element</a>
in sceneggiatura:
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);
}
}
Ciò consente all'utente di scorrere direttamente all'elemento, se l'utente atterra direttamente sulla pagina con hashtag in url.
Ma in questo caso, ho sottoscritto il frammento di route in ngAfterViewChecked
ma ngAfterViewChecked()
viene chiamato continuamente per ogni ngDoCheck
e non consente all'utente di tornare indietro, così routeFragmentSubscription.unsubscribe
viene chiamato dopo un timeout di 0 millis dopo che la vista è stata spostata sull'elemento.
Inoltre, il metodo gotoHashtag
è definito per scorrere fino all'elemento quando l'utente fa clic in modo specifico sul tag di ancoraggio.
Aggiornamento:
Se url ha querystrings, [routerLink]="['self-route', id]"
in anchor non conserverà le querystring. Ho provato a seguire la soluzione alternativa per lo stesso:
<a (click)="gotoHashtag('some-element')">Jump to Element</a>
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);
}
Questo lavoro per me !! Questo ngPer così dinamicamente ancora il tag, è necessario attendere il rendering
HTML:
<div #ngForComments *ngFor="let cm of Comments">
<a id="Comment_{{cm.id}}" fragment="Comment_{{cm.id}}" (click)="jumpToId()">{{cm.namae}} Reply</a> Blah Blah
</div>
Il mio file ts:
private fragment: string;
@ViewChildren('ngForComments') AnchorComments: QueryList<any>;
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();
}
}
Non dimenticare di importare ViewChildren
, QueryList
ecc. E aggiungere qualche costruttore ActivatedRoute
!!
Ho avuto lo stesso problema. La soluzione: utilizzando View port Scroller https://angular.io/api/common/ViewportScroller#scrolltoanchor
- codice app-routing.module.ts:
import { PageComponent } from './page/page.component';
const routes: Routes = [
path: 'page', component: PageComponent },
path: 'page/:id', component: PageComponent }
];
- HTML componente
<a (click) = "scrollTo('typeExec')">
<mat-icon>lens</mat-icon>
</a>
- Codice del componente:
import { Component } from '@angular/core';
import { ViewportScroller } from '@angular/common';
export class ParametrageComponent {
constructor(private viewScroller: ViewportScroller) {}
scrollTo(tag : string)
{
this.viewScroller.scrollToAnchor(tag);
}
}
A differenza di altre risposte, aggiungerei anche focus()
insieme a scrollIntoView()
. Inoltre sto usando setTimeout
dato che salta in alto altrimenti quando si cambia l'URL. Non sono sicuro di quale sia stata la ragione, ma sembra che setTimeout
risolva il problema.
Origine:
<a [routerLink] fragment="some-id" (click)="scrollIntoView('some-id')">Jump</a>
Destinazione:
<a id="some-id" tabindex="-1"></a>
Dattiloscritto:
scrollIntoView(anchorHash) {
setTimeout(() => {
const anchor = document.getElementById(anchorHash);
if (anchor) {
anchor.focus();
anchor.scrollIntoView();
}
});
}
Ho provato la maggior parte di queste soluzioni, ma ho riscontrato problemi nell'andare e tornare con un altro frammento che non avrebbe funzionato, quindi ho fatto qualcosa di un po 'diverso che funziona al 100% e mi sbarazza del brutto hash nell'URL.
tl; dr, ecco un modo migliore di quello che ho visto finora.
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();
}
}
Ho appena testato plugin molto utili disponibili in nmp - ngx-scroll-to , che funziona benissimo per me. Tuttavia è progettato per Angular 4+, ma forse qualcuno troverà utile questa risposta.