it-swarm.com.de

Angular2 Routing mit Hashtag zum Seitenanker

Ich möchte meiner Angular2-Seite einige Links hinzufügen, die beim Klicken zu bestimmten Positionen springen innerhalb dieser Seite, so wie es normale Hashtags tun. Die Links wären also so ähnlich

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

usw.

Ich glaube nicht, dass ich HashLocationStrategy brauche, da ich mit der normalen Angular2-Methode gut zurechtkomme, aber wenn ich direkt hinzufüge, springt der Link tatsächlich zum Stammverzeichnis, nicht irgendwo auf derselben Seite. Jede Richtung wird geschätzt, danke.

105
Stone

Update

Dies wird jetzt unterstützt

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

Fügen Sie den folgenden Code zu Ihrer Komponente hinzu, um einen Bildlauf durchzuführen

  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

Dies ist ein bekanntes Problem und wird unter https://github.com/angular/angular/issues/6595 verfolgt

105

Obwohl Günters Antwort richtig ist, deckt es nicht den "Sprung zu" dem Ankertag-Teil ab.

Deshalb zusätzlich zu:

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

... in der Komponente (Parent), in der Sie ein "Jump to" -Verhalten benötigen, Folgendes hinzufügen:

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

  }
}

Bitte beachten Sie, dass dies eine Problemumgehung ist ! Folgen Sie dieses Github-Problem für zukünftige Updates. Dank an Victor Savkin für die Bereitstellung der Lösung!

48
Kaloyan Kosev

Tut mir leid, dass ich es etwas spät beantworte. Es gibt eine vordefinierte Funktion in der Angular= Routing Documentation, die uns beim Routing mit einem Hashtag zum Seitenanker hilft, dh anchorScrolling: 'enabled'

Schritt-1: - Importieren Sie zuerst das RouterModule in das app.module .ts Datei: -

imports:[ 
    BrowserModule, 
    FormsModule,
    RouterModule.forRoot(routes,{
      anchorScrolling: 'enabled'
    })
  ],

Schritt-2: - Gehen Sie zur HTML-Seite, erstellen Sie die Navigation und fügen Sie zwei wichtige Attribute wie [routerLink] hinzu und Fragment zum Abgleichen der jeweiligen Div IDs : -

<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>

Schritt 3: - Erstellen Sie einen Abschnitt/ein Div, indem Sie den ID-Namen mit dem Fragment : -

<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>

Als Referenz habe ich das folgende Beispiel hinzugefügt, indem ich eine kleine Demo erstellt habe, die zur Lösung Ihres Problems beiträgt.

Demo: https://routing-hashtag-page-anchors.stackblitz.io/

25
Naheed Shareef

Ein bisschen spät, aber hier ist eine Antwort, die funktioniert:

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

Und in der Komponente:

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

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

Der obige Befehl scrollt nicht automatisch zur Ansicht, wenn Sie bereits auf einer Seite mit einem Anker landen. Daher habe ich die obige Lösung in meinem ngInit verwendet, damit dies auch funktioniert:

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

Stellen Sie sicher, dass Sie Router, ActivatedRoute und NavigationEnd am Anfang Ihrer Komponente importieren.

Quelle

25
Ali Bari

Keine der vorherigen Antworten hat für mich funktioniert. In einem letzten Versuch habe ich es mit meiner Vorlage versucht:

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

Damit in meinem .ts:

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

Und es funktioniert wie erwartet für interne Links. In diesem Fall werden keine Ankertags verwendet, sodass die URL überhaupt nicht berührt wird.

18
Wenqin Chen

Die obigen Lösungen haben bei mir nicht funktioniert ... Diese haben es geschafft:

Bereiten Sie zunächst MyAppComponent für das automatische Scrollen in ngAfterViewChecked () ... vor.

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

              }
            }
          } );
    }

  }

}

Navigieren Sie dann zu my-app-route Senden von 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 } );
  }

}
6
JavierFuentes

Alle anderen Antworten funktionieren mit Angular Version <6.1. Wenn Sie jedoch die neueste Version haben, müssen Sie diese hässlichen Hacks nicht ausführen, da Angular das Problem behoben hat.

hier ist der Link zum Thema

Sie müssen lediglich scrollOffset mit der Option des zweiten Arguments der Methode RouterModule.forRoot Festlegen.

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

Verwenden Sie dies für das Router-Modul in app-routing.module.ts:

@NgModule({
  imports: [RouterModule.forRoot(routes, {
    useHash: true,
    scrollPositionRestoration: 'enabled',
    anchorScrolling: 'enabled',
    scrollOffset: [0, 64]
  })],
  exports: [RouterModule]
})

Dies wird in Ihrem HTML sein:

<a href="#/users/123#userInfo">
4
faizal razak

Wenn Sie Kalyoyans Antwort hinzufügen, ist dieses Abonnement an den Router gebunden und wird so lange ausgeführt, bis die Seite vollständig aktualisiert ist. Wenn Sie Router-Ereignisse in einer Komponente abonnieren, müssen Sie sich unbedingt von ngOnDestroy abmelden:

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

Ich habe es gerade auf meiner eigenen Website zum Laufen gebracht und dachte mir, dass es sich lohnen würde, meine Lösung hier zu veröffentlichen.

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

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

Stellen Sie dann sicher, dass Sie Folgendes in Ihre Komponente einfügen:

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

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

Da die fragment -Eigenschaft immer noch keinen Anker-Bildlauf bietet, hat diese Problemumgehung den Trick für mich ausgeführt:

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

Nachdem ich alle Lösungen gelesen hatte, suchte ich nach einer Komponente und fand eine, die genau das tut, was die ursprüngliche Frage verlangte: Scrollen, um Links zu verankern. https://www.npmjs.com/package/ng2-scroll-to

Bei der Installation verwenden Sie folgende Syntax:

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

Es hat wirklich gut für mich funktioniert.

3
John

Eine einfache Lösung, die für Seiten ohne Abfrageparameter funktioniert, ist Browser-kompatibel (vorwärts/rückwärts), Router-kompatibel und Deep-Link-kompatibel.

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

Das Zeitlimit dient lediglich dazu, der Seite das Laden von dynamischen Daten zu ermöglichen, die durch eine * ngIf "geschützt" sind. Dies kann auch verwendet werden, um beim Ändern der Route an den oberen Rand der Seite zu scrollen. Geben Sie einfach ein Standard-Tag für den oberen Anker an.

2

Hier ist eine weitere Problemumgehung mit Bezug auf JavierFuentes Antwort:

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

im Drehbuch:

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

Dadurch kann der Benutzer direkt zum Element scrollen, wenn er direkt auf der Seite mit dem Hashtag in der URL landet.

Aber in diesem Fall habe ich route Fragment in ngAfterViewChecked abonniert, aber ngAfterViewChecked() wird fortlaufend pro ngDoCheck aufgerufen, und der Benutzer kann nicht nach oben scrollen routeFragmentSubscription.unsubscribe Wird nach einem Timeout von 0 Millis aufgerufen, nachdem die Ansicht zum Element gescrollt wurde.

Zusätzlich wird die Methode gotoHashtag definiert, um zum Element zu scrollen, wenn der Benutzer speziell auf das Ankertag klickt.

pdate:

Wenn die URL Querystringe enthält, werden die Querystringe durch [routerLink]="['self-route', id]" Im Anker nicht beibehalten. Ich habe folgende Problemumgehung aus dem gleichen Grund versucht:

<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
Vicky Gonsalves

Dieser arbeitet für mich !! Um das Tag dynamisch zu verankern, müssen Sie warten, bis sie gerendert sind

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>

Meine ts-Datei:

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

Vergiss nicht, das ViewChildren, QueryList usw. zu importieren und einen Konstruktor ActivatedRoute hinzuzufügen !!

1
John Connor

Ich hatte das gleiche Problem. Die Lösung: Verwenden von View Port Scroller https://angular.io/api/common/ViewportScroller#scrolltoanchor

- app-routing.module.ts code:

import { PageComponent } from './page/page.component';

const routes: Routes = [
   path: 'page', component: PageComponent },
   path: 'page/:id', component: PageComponent }
];

- Komponenten-HTML

  <a (click) = "scrollTo('typeExec')">
    <mat-icon>lens</mat-icon>
  </a>

- Code der Komponente:

    import { Component } from '@angular/core';
    import { ViewportScroller } from '@angular/common';


    export class ParametrageComponent {

      constructor(private viewScroller: ViewportScroller) {}

      scrollTo(tag : string)
      {
        this.viewScroller.scrollToAnchor(tag);
      }

    }
1

Im Gegensatz zu anderen Antworten würde ich zusätzlich auch focus() zusammen mit scrollIntoView() hinzufügen. Außerdem verwende ich setTimeout, da es sonst beim Ändern der URL nach oben springt. Ich bin mir nicht sicher, was der Grund dafür war, aber es scheint, dass setTimeout die Problemumgehung ausführt.

Ursprung:

<a [routerLink] fragment="some-id" (click)="scrollIntoView('some-id')">Jump</a>

Ziel:

<a id="some-id" tabindex="-1"></a>

Typoskript:

scrollIntoView(anchorHash) {
    setTimeout(() => {
        const anchor = document.getElementById(anchorHash);
        if (anchor) {
            anchor.focus();
            anchor.scrollIntoView();
        }
    });
}
0
Hrvoje Golcic

Ich habe die meisten dieser Lösungen ausprobiert, aber es gab Probleme beim Verlassen und Zurückkehren mit einem anderen Fragment, das nicht funktioniert. Deshalb habe ich etwas anderes gemacht, das zu 100% funktioniert und den hässlichen Hash in der URL beseitigt.

hier ist ein besserer Weg als das, was ich bisher gesehen habe.

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

Ich habe gerade ein sehr nützliches Plugin getestet, das in nmp - ngx-scroll-to verfügbar ist, was für mich großartig funktioniert. Es ist jedoch für Angular 4+ konzipiert, aber vielleicht findet jemand diese Antwort hilfreich.

0
mpro