2016-03-19 1 views
51

Je souhaite ajouter des liens sur ma page Angular2, qui, lorsque vous cliquez, passera à des positions spécifiques dans cette page, comme le font les hashtags normaux. Ainsi, les liens seraient quelque chose commeAngular2 Routage avec Hashtag à la page d'ancrage

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

etc.

Je ne pense pas avoir besoin HashLocationStrategy, comme je suis très bien avec la façon dont Angular2 normale, mais si j'ajoute directement, le lien REELLEMENT sauter à la racine, pas quelque part sur la même page. Toute direction est appréciée, merci.

Répondre

58

Mise à jour

Ceci est maintenant pris en charge

<a [routerLink]="['somepath']" fragment="Test">Jump to 'Test' anchor </a> 
this._router.navigate(['/somepath', id ], {fragment: 'test'}); 

** Ajouter ci-dessous le code à votre composant pour faire défiler **

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

Ceci est un problème connu et objet d'un suivi à https://github.com/angular/angular/issues/6595

+0

à quoi fait référence 'id'? – invot

+1

@invot une variable avec une chaîne (par exemple ce que 123' est dans la question) en supposant que le chemin d'accès attend un paramètre comme '{path: 'users /: id', ....}' –

+0

Que faire si je Voulez-vous que l'option #anchor apparaisse uniquement si le fragment n'est pas null/indéfini? Actuellement, j'ai toujours http://somethi.ng/page#. S'il n'y a pas d'ancre, elle devrait juste être http://somethi.ng/page. S'il y a une ancre, elle devrait toujours être http://somethi.ng/page#anchor. – Kunepro

27

Bien que Günter's answer est correct, il ne couvre pas le « saut » la partie de balise d'ancrage.

Par conséquent, en plus de:

<a [routerLink]="['somepath']" fragment="Test">Jump to 'Test' anchor </a> 
this._router.navigate(['/somepath', id ], {fragment: 'test'}); 

... dans le composant (parent) où vous avez besoin d'un "saut" comportement, ajouter:

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); } 
     } 
     } 
    }); 

    } 
} 

S'il vous plaît noter que ceci est une solution de contournement! Suivez this github issue pour les mises à jour futures. Crédits à Victor Savkin pour fournir la solution!

+0

cela fonctionne. merci – ruucm

+0

Je n'ai aucun doute. –

+1

cela fonctionne, vous avez sauvé ma journée, merci! – Torai

11

Un peu en retard, mais voici une réponse que je trouve qui fonctionne:

<a [routerLink]="['/path']" fragment="test (click)="onAnchorClick()">Anchor</a> 

Et dans le composant:

constructor(private route: ActivatedRoute, private router: Router) {} 

    onAnchorClick () { 
    this.route.fragment.subscribe (f => { 
     const element = document.querySelector ("#" + f) 
     if (element) element.scrollIntoView (element) 
    }); 
    } 

Ce qui précède défile pas automatiquement à la vue si vous atterrissez sur une page avec une ancre déjà, donc j'ai utilisé la solution ci-dessus dans mon ngInit afin qu'il puisse travailler avec ça aussi:

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); } 
     } 
     } 
    }); 
    } 

Assurez-vous d'importer Router, ActivatedRoute et NavigationEnd au début de votre composant et tout devrait être bon.

Source

+0

Fonctionne pour moi! Si vous voulez naviguer dans la même page que vous êtes déjà sur, utilisez [routerLink] = "['.']" – Raoul

2

solutions ci-dessus ne fonctionne pas pour moi ...Celui-ci l'a fait:

D'abord, préparer MyAppComponent pour le défilement automatique dans 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); 

       } 
      } 
      }); 
    } 

    } 

} 

Ensuite, accédez à my-app-route envoyer prodID hashtag

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 }); 
    } 

} 
+0

Votez en l'absence de commentaire ou d'explication ... ce n'est pas une bonne pratique ... – JavierFuentes

+0

Merci Voter, signifie que cela a fonctionné pour vous aussi ... cette solution a vraiment sauvé ma journée! – JavierFuentes

+0

Cela a fonctionné pour moi! Pourquoi utiliser ngAfterViewChecked au lieu de ngInit? –

0

Voici une autre solution de contournement avec référence à JavierFuentes réponse:

<a [routerLink]="['self-route', id]" fragment="some-element" (click)="gotoHashtag('some-element')">Jump to Element</a> 

dans le 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); 
     } 
} 

Cela permet à l'utilisateur de faire défiler directement à l'élément, si l'utilisateur atterrit directement sur la page ayant hashtag dans l'URL.

Mais dans ce cas, je suis abonné à la route Fragment ngAfterViewChecked mais ngAfterViewChecked() est appelé en permanence par tous les ngDoCheck et il ne permet à l'utilisateur de faire défiler en arrière vers le haut, de sorte routeFragmentSubscription.unsubscribe est appelé après un délai d'attente de 0 Millis après vue est fait défiler jusqu'à l'élément.

De plus, la méthode gotoHashtag est définie pour faire défiler jusqu'à l'élément lorsque l'utilisateur clique spécifiquement sur l'étiquette d'ancrage.

Mise à jour:

Si url a querystrings, [routerLink]="['self-route', id]" dans l'ancre chasse gardée wont les querystrings. J'ai essayé de solution suivante pour le même:

<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); 
} 
1

Puisque la propriété fragment ne fournit toujours pas le défilement d'ancrage, cette solution de contournement a fait l'affaire pour moi:

<div [routerLink]="['somepath']" fragment="Test"> 
    <a href="#Test">Jump to 'Test' anchor </a> 
</div> 
2

Ajout à son answer Kalyoyan, cet abonnement est lié au routeur et continuera jusqu'à ce que la page soit complètement actualisée. Lorsque vous vous abonnez au routeur d'événements dans un composant, assurez-vous de résilier votre abonnement à 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(); 
    } 
} 
1

Je viens de recevoir ce travail sur mon propre site web, donc je me suis dit qu'il serait utile de poster ma solution ici.

<a [routerLink]="baseUrlGoesHere" fragment="nameOfYourAnchorGoesHere">Link Text!</a> 

<a name="nameOfYourAnchorGoesHere"></a> 
<div>They're trying to anchor to me!</div> 

Et puis dans votre composant, assurez-vous d'inclure ceci:

import { ActivatedRoute } from '@angular/router'; 

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

Je pense qu'il est préférable d'écrire juste 'element.scrollIntoView()' ou 'element.scrollIntoView (true)'. Votre version n'a pas compilé pour moi (peut-être à cause de strictNullChecks?). –

3

Après avoir lu toutes les solutions, je cherchais un composant et j'ai trouvé un qui fait exactement ce que la question initiale a demandé pour: faire défiler les liens d'ancrage. https://www.npmjs.com/package/ng2-scroll-to

Lorsque vous installez, vous utilisez la syntaxe comme:

// 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 { 
} 

Il a travaillé très bien pour moi.

+0

Utilisez la roue, ne l'inventez pas à nouveau;) –

+0

Avez-vous regardé le code derrière ce composant?Il semble très fragile - le projet a également 14 problèmes ouverts - qui incluent des choses comme des éléments inexistants, des cibles nulles, pas de défilement vers l'élément, des problèmes de support du navigateur. – Ryan

3

Aucune des réponses précédentes n'a fonctionné pour moi.Dans un dernier effort, j'ai essayé dans mon modèle:

<a (click)="onClick()">From Here</a> 
<div id='foobar'>To Here</div> 

Avec dans mes .ts:

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

Et il fonctionne comme prévu pour les liens internes. Cela n'utilise pas réellement les balises d'ancrage afin de ne pas toucher à l'URL du tout.

0

Une solution simple qui fonctionne pour les pages sans aucun paramètre de requête, est le navigateur avant/arrière, le routeur et les liens profonds compatibles.

<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(); 
    } 
} 

Le délai d'expiration est simplement de permettre à la page de charger des données dynamiques "protégées" par un * ngIf. Cela peut également être utilisé pour faire défiler vers le haut de la page lors du changement de route - il suffit de fournir une balise d'ancrage supérieure par défaut.

0

Je viens de tester un plugin très utile disponible en nmp - ngx-scroll-to, ce qui fonctionne très bien pour moi. Cependant, il est conçu pour Angular 4+, mais peut-être que quelqu'un trouvera cette réponse utile.