Angular2 การกำหนดเส้นทางด้วย Hashtag ไปยังจุดยึดหน้า


120

ฉันต้องการเพิ่มลิงก์ในหน้า Angular2 ของฉันซึ่งเมื่อคลิกจะข้ามไปยังตำแหน่งเฉพาะในหน้านั้นเช่นเดียวกับที่แฮชแท็กปกติทำ ดังนั้นลิงก์จะเป็นอย่างไร

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

เป็นต้น

ฉันไม่คิดว่าฉันต้องการ HashLocationStrategy เพราะฉันสบายดีกับวิธี Angular2 ปกติ แต่ถ้าฉันเพิ่มโดยตรงลิงก์จะข้ามไปที่รูทจริง ๆ ไม่ใช่ที่ใดที่หนึ่งในหน้าเดียวกัน ขอบคุณทุกทิศทางขอบคุณ

คำตอบ:


130

ปรับปรุง

ตอนนี้รองรับแล้ว

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

เพิ่มโค้ดด้านล่างลงในส่วนประกอบของคุณเพื่อเลื่อน

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

เป็นต้นฉบับ

นี่เป็นปัญหาที่ทราบและติดตามได้ที่https://github.com/angular/angular/issues/6595


1
@invot ตัวแปรที่มีสตริง (เช่นสิ่งที่123อยู่ในคำถาม) สมมติว่าเส้นทางเส้นทางคาดว่าจะมีพารามิเตอร์เช่น{ path: 'users/:id', ....}
GünterZöchbauer

2
หากคุณต้องการเลื่อนไปที่จุดยึดโปรดดูโพสต์นี้: github.com/angular/angular/issues/6595
Pere Pages

12
หมายเหตุ: ยังไม่เลื่อน
Martín Coll

2
ใช่มันใช้งานได้กับ setTimeout ถ้าฉันพบทางออกที่ดีกว่าฉันจะแจ้งให้คุณทราบ
Amr Ibrahim

1
สำหรับผู้ที่มีปัญหาในการเลื่อนไปยังรหัสที่มีลักษณะเหมือนตัวเลขให้นึกถึงตัวเลือก CSS ที่ถูกต้อง01หรือ100ไม่ คุณอาจต้องการเพิ่มตัวอักษรหรืออะไรบางอย่างเพื่อให้เป็นตัวเลือกที่ถูกต้อง ดังนั้นคุณจะยังคงส่งผ่าน01เป็นส่วนหนึ่ง แต่idจะต้องมีบางอย่างที่เหมือนd01และจึงdocument.querySelector('#d'+id)จะตรงกัน
ป๊อป

52

ถึงแม้ว่าคำตอบGünterของถูกต้องมันไม่ครอบคลุม "ข้ามไปส่วน"

ดังนั้นนอกจากนี้เพื่อ:

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

... ในองค์ประกอบ (ระดับบนสุด) ที่คุณต้องการพฤติกรรม "ข้ามไปที่" ให้เพิ่ม:

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

  }
}

โปรดทราบว่านี่เป็นวิธีแก้ปัญหาชั่วคราว ! ติดตามปัญหา github นี้สำหรับการอัปเดตในอนาคต ให้เครดิตกับVictor Savkinในการจัดหาโซลูชัน!


สวัสดีฉันกำลังสร้างหน้าคำถามที่พบบ่อยซึ่งคุณสามารถข้ามไปที่คำตอบได้โดยคลิกที่คำถามที่กำหนดไว้ในรายการที่ด้านบนของหน้า ดังนั้นผู้ใช้จึงอยู่ในหน้าปัจจุบันเมื่อเขากระโดดไปที่จุดยึด หากฉันต้องการให้แอตทริบิวต์ routerLink ทำงานฉันต้องให้"['../faq']"เป็นค่ามิฉะนั้นจะพยายามข้ามไปที่ / faq / faq / # anchor insteaf ของ / faq / # anchor นี่เป็นวิธีที่ถูกต้องในการทำหรือมีวิธีที่หรูหรากว่าในการอ้างถึงหน้าปัจจุบันใน routerlink หรือไม่? นอกจากนี้ยังแจ้งdocument.querySelector("#" + tree.fragment);ข้อผิดพลาดตัวเลือกที่ไม่ถูกต้อง คุณแน่ใจหรือไม่ว่าถูกต้องขอบคุณ
มอริซ

2
เมื่อคุณคลิกอีกครั้งบนลิงก์เดิมมันจะไม่ทำงาน ใครไม่ทำให้งานนี้ถ้าผู้ใช้คลิกที่ลิงค์สมอเดียวกัน<a [routerLink]="['/faq']" fragment="section6">?
Junior Mayhé

@JuniorM คุณเคยคิดออกไหม? ฉันพบปัญหาเดียวกัน
The Muffin Man

@ มัฟฟินลองของแบบนี้github.com/juniormayhe/Mailing/blob/master/Mailing.SPA/src/app/…
Junior Mayhé

1
สิ่งนี้ต้องการการเปิดรับมากขึ้น นี่คือคำตอบที่ดีกว่า IMO คนส่วนใหญ่จะต้องการข้ามไปยังส่วน
iamtravisw

45

ขออภัยที่ตอบช้าไปหน่อย มีฟังก์ชันที่กำหนดไว้ล่วงหน้าในเอกสารการกำหนดเส้นทางเชิงมุมซึ่งช่วยเราในการกำหนดเส้นทางด้วยแฮชแท็กไปยังจุดยึดหน้าเช่น anchorScrolling: 'enable'

ขั้นตอนที่ 1: -นำเข้าRouterModuleในไฟล์ app.module.ts ก่อน: -

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

ขั้นตอนที่ 2: -ไปที่หน้า HTML, สร้างนำทางและเพิ่มสองคุณลักษณะที่สำคัญเช่น[routerLink]และชิ้นส่วนสำหรับการจับคู่ตามลำดับ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>

ขั้นตอนที่ 3: -สร้างส่วน / div โดยจับคู่ชื่อ IDกับส่วนย่อย : -

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

สำหรับการอ้างอิงของคุณฉันได้เพิ่มตัวอย่างด้านล่างโดยสร้างการสาธิตขนาดเล็กซึ่งช่วยแก้ปัญหาของคุณ

การสาธิต: https://routing-hashtag-page-anchors.stackblitz.io/


ขอบคุณมากสำหรับสิ่งนี้ สะอาดกระชับและใช้งานได้จริง!
Belmiris

2
ใช่ขอบคุณสำหรับการเลื่อนอัตโนมัติในการโหลดหน้าด้วย Angular 7 เพียงแค่ต้องเพิ่มscrollPositionRestoration: 'enabled',ภายใต้ตัวเลือก anchorScrolling :)
Mickaël

สิ่งนี้ต่อท้ายแฮชต่อท้าย url ของฉันอย่างถูกต้อง แต่ไม่ยึดกับ div ที่มีชื่อเดียวกัน ฉันไม่แน่ใจว่าฉันขาดอะไรไป ฉันทำตามสามขั้นตอนข้างต้น
oaklandrichie

@oaklandrichie: คุณสามารถแชร์รหัส jsfiddle / stackblitz ได้ที่นี่ ฉันช่วยคุณได้
Naheed Shareef

คำตอบนี้ควรได้รับการยอมรับอย่างแน่นอนใช้งานได้อย่างมีเสน่ห์!
Kiss Koppány

25

สายไปหน่อย แต่นี่คือคำตอบที่ฉันพบว่าใช้งานได้:

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

และในส่วนประกอบ:

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

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

ด้านบนไม่ได้เลื่อนไปที่มุมมองโดยอัตโนมัติหากคุณเข้าสู่หน้าที่มีจุดยึดอยู่แล้วดังนั้นฉันจึงใช้วิธีแก้ปัญหาข้างต้นใน ngInit ของฉันเพื่อให้สามารถทำงานกับสิ่งนั้นได้เช่นกัน:

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

อย่าลืมนำเข้าเราเตอร์ ActivatedRoute และ NavigationEnd ที่จุดเริ่มต้นของคอมโพเนนต์ของคุณและมันควรจะดีไป

แหล่ง


4
ใช้ได้ผลสำหรับฉัน! ในกรณีที่คุณต้องการนำทางภายในเพจเดิมที่คุณใช้งานอยู่ให้ใช้ [routerLink] = "['.']"
Raoul

1
ช่วยอธิบายเพิ่มเติมได้ไหม ส่วนนี้document.querySelector ( "#" + f )ทำให้ฉันมีข้อผิดพลาดเนื่องจากคาดว่าจะมีตัวเลือกไม่ใช่สตริง
Maurice

1
@Maurice สำหรับฉันมันใช้งานได้: element.scrollIntoView()(โดยไม่ต้องส่งผ่านelementไปยังฟังก์ชัน) element.scrollIntoView({block: "end", behavior: "smooth"})ที่จะทำให้มันเรียบใช้นี้
นายบี

Intellisense นี่แสดงให้เห็นว่าภายในonAnchorClick()เราจะต้องผ่านบูลเพื่อ if (element) { element.scrollIntoView(true); }scrollIntoView: ตอนนี้ฉันสามารถคลิกสองครั้งในลิงค์เดียวกันและเลื่อนงาน
Junior Mayhé

18

ไม่มีคำตอบก่อนหน้านี้สำหรับฉัน ในความพยายามครั้งสุดท้ายฉันได้ลองใช้เทมเพลตของฉัน:

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

ด้วยสิ่งนี้ใน. ts ของฉัน:

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

และทำงานได้ตามที่คาดไว้สำหรับลิงก์ภายใน สิ่งนี้ไม่ได้ใช้แท็กจุดยึดดังนั้นมันจะไม่สัมผัสกับ URL เลย


1
ง่ายและสะดวก
WasiF

6

วิธีแก้ปัญหาข้างต้นไม่ได้ผลสำหรับฉัน ... อันนี้ทำ:

ขั้นแรกเตรียมความพร้อมMyAppComponentสำหรับการเลื่อนอัตโนมัติใน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 );

              }
            }
          } );
    }

  }

}

จากนั้นไปที่my-app-routeการส่ง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 } );
  }

}

ลงคะแนนโดยไม่มีความเห็นหรือคำอธิบาย ... มันไม่ใช่แนวปฏิบัติที่ดี ...
JavierFuentes

สิ่งนี้ได้ผลสำหรับฉัน! ทำไมต้องใช้ ngAfterViewChecked แทน ngInit
Antoine Boisier-Michaud

ขอบคุณ @ AntoineBoisier-Michaud สำหรับฟีดแบ็กในเชิงบวกของคุณ ngInit ไม่ทำงานเสมอที่ฉันต้องการในโครงการของฉัน ... ngAfterViewChecked ทำ คุณสามารถโหวตวิธีแก้ปัญหานี้ได้หรือไม่? ขอบคุณ
JavierFuentes

6

คำตอบอื่น ๆ ทั้งหมดจะใช้ได้กับ Angular เวอร์ชัน <6.1 แต่ถ้าคุณมีเวอร์ชันล่าสุดคุณก็ไม่จำเป็นต้องทำการแฮ็กที่น่าเกลียดเหล่านี้เนื่องจาก Angular ได้แก้ไขปัญหาแล้ว

นี่คือลิงค์สำหรับปัญหา

สิ่งที่คุณต้องทำคือตั้งค่าscrollOffsetด้วยตัวเลือกของอาร์กิวเมนต์ที่สองของRouterModule.forRootวิธีการ

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

2
สิ่งนี้จะใช้ได้กับลิงก์ภายนอกหรือไม่ พูดจากเว็บไซต์อื่นฉันคลิกที่ www.abc.com # sectionToScrollTo
Sushmit Sagar

anchorScrolling ไม่ทำงานหากคุณใช้ * ngIf อย่างกว้างขวางเนื่องจากมันข้ามไปที่ต้น :-(
Jojo.Lechelt

ปัญหาเดียวที่ฉันมีกับเรื่องนี้คือเวลา - มันมีแนวโน้มที่จะข้ามไปที่จุดยึดก่อนที่สไตล์ขององค์ประกอบบางส่วนจะแสดงผลเต็มรูปแบบทำให้การวางตำแหน่งไม่อยู่ จะดีมากถ้าคุณสามารถเพิ่มความล่าช้า :)
Charly

5

ใช้สิ่งนี้สำหรับโมดูลเราเตอร์ในapp-routing.module.ts:

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

สิ่งนี้จะอยู่ใน HTML ของคุณ:

<a href="#/users/123#userInfo">

4

ในไฟล์ html:

<a [fragment]="test1" [routerLink]="['./']">Go to Test 1 section</a>

<section id="test1">...</section>
<section id="test2">...</section>

ในไฟล์ ts:

export class PageComponent implements AfterViewInit, OnDestroy {

  private destroy$$ = new Subject();
  private fragment$$ = new BehaviorSubject<string | null>(null);
  private fragment$ = this.fragment$$.asObservable();

  constructor(private route: ActivatedRoute) {
    this.route.fragment.pipe(takeUntil(this.destroy$$)).subscribe(fragment => {
      this.fragment$$.next(fragment);
    });
  }

  public ngAfterViewInit(): void {
    this.fragment$.pipe(takeUntil(this.destroy$$)).subscribe(fragment => {
      if (!!fragment) {
        document.querySelector('#' + fragment).scrollIntoView();
      }
    });
  }

  public ngOnDestroy(): void {
    this.destroy$$.next();
    this.destroy$$.complete();
  }
}

สามารถใส่ querySelector ลงใน directive ชื่อ scrolllTo ได้อย่างง่ายดาย URL จะเป็น <a scrollTo="test1" [routerLink]="['./']"> ไปที่ส่วนทดสอบ 1 </a>
John Peters

3

เนื่องจากคุณสมบัติการแยกส่วนยังไม่มีการเลื่อนจุดยึดวิธีแก้ปัญหานี้จึงเป็นเคล็ดลับสำหรับฉัน:

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

3

การเพิ่มคำตอบของ Kalyoyan การสมัครสมาชิกนี้จะเชื่อมโยงกับเราเตอร์และจะใช้งานได้จนกว่าเพจจะรีเฟรชอย่างสมบูรณ์ เมื่อสมัครรับข้อมูลเหตุการณ์เราเตอร์ในคอมโพเนนต์อย่าลืมยกเลิกการสมัครใน 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();
  }
}

ฉันคิดว่าการสมัครรับข้อมูลเส้นทางเหตุการณ์ถูกตัดขาดโดยอัตโนมัติ

3

ฉันเพิ่งทำงานนี้บนเว็บไซต์ของตัวเองดังนั้นฉันคิดว่ามันคุ้มค่าที่จะโพสต์วิธีแก้ปัญหาของฉันที่นี่

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

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

จากนั้นในส่วนประกอบของคุณตรวจสอบให้แน่ใจว่าคุณได้รวมสิ่งนี้:

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

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

ผมคิดว่ามันจะดีกว่าที่จะเขียนเพียงหรือelement.scrollIntoView() element.scrollIntoView(true)เวอร์ชันของคุณไม่ได้รวบรวมให้ฉัน (อาจเป็นเพราะเข้มงวด NullChecks?)
David L.

3

หลังจากอ่านวิธีแก้ปัญหาทั้งหมดแล้วฉันก็มองหาส่วนประกอบและพบสิ่งที่ตอบสนองคำถามเดิมที่ถาม: การเลื่อนไปยังจุดเชื่อมโยง https://www.npmjs.com/package/ng2-scroll-to

เมื่อคุณติดตั้งคุณใช้ไวยากรณ์เช่น:

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

มันได้ผลดีสำหรับฉันจริงๆ


ใช้ล้ออย่าประดิษฐ์อีก;)
Yogen Rai

คุณได้ดูโค้ดเบื้องหลังส่วนประกอบนั้นแล้วหรือยัง? มันดูเปราะบางมาก - โครงการยังมีปัญหาที่เปิดอยู่ 14 ประเด็นซึ่งรวมถึงสิ่งต่างๆเช่นองค์ประกอบที่ไม่มีอยู่เป้าหมายเป็นโมฆะไม่เลื่อนไปที่องค์ประกอบปัญหาการสนับสนุนเบราว์เซอร์
Drenai

ไม่ทำงานเมื่อคุณมีลูก (ลูกมีเอนทิตีและ / หรือชื่อจุดยึด) ในองค์ประกอบหลักมันเพิ่งรีเฟรชหน้า
Sasha Bond

3

วิธีง่ายๆที่ใช้ได้กับเพจที่ไม่มีพารามิเตอร์การค้นหาใด ๆ คือเบราว์เซอร์ย้อนกลับ / ไปข้างหน้าเราเตอร์และการเชื่อมโยงในรายละเอียด

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

การหมดเวลาเป็นเพียงเพื่อให้หน้าโหลดข้อมูลไดนามิกที่ "ป้องกัน" โดย * ngIf นอกจากนี้ยังสามารถใช้เพื่อเลื่อนไปที่ด้านบนสุดของหน้าเมื่อเปลี่ยนเส้นทาง - เพียงแค่ระบุแท็กจุดยึดด้านบนเริ่มต้น


2

หากไม่จำเป็นต้องมีการต่อท้ายรหัสองค์ประกอบเหล่านั้นคุณควรพิจารณาดูที่ลิงก์นี้:

Angular 2 - เชื่อมโยงไปยังองค์ประกอบในหน้าปัจจุบัน

// html
// add (click) event on element
<a (click)="scroll({{any-element-id}})">Scroll</a>

// in ts file, do this
scroll(sectionId) {
let element = document.getElementById(sectionId);

  if(element) {
    element.scrollIntoView(); // scroll to a particular element
  }
 }


1

นี่คือวิธีแก้ปัญหาอื่นที่มีการอ้างอิงถึงคำตอบของ JavierFuentes:

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

ในสคริปต์:

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

สิ่งนี้ช่วยให้ผู้ใช้สามารถเลื่อนไปที่องค์ประกอบได้โดยตรงหากผู้ใช้ไปที่หน้าที่มีแฮชแท็กใน url โดยตรง

แต่ในกรณีนี้ฉันได้สมัครรับเส้นทาง Fragment แล้วngAfterViewCheckedแต่ngAfterViewChecked()ถูกเรียกอย่างต่อเนื่องต่อทุกๆngDoCheckและไม่อนุญาตให้ผู้ใช้เลื่อนกลับไปด้านบนดังนั้นrouteFragmentSubscription.unsubscribeเรียกว่าหลังจากหมดเวลา 0 มิลลิวินาทีหลังจากที่มุมมองเลื่อนไปที่องค์ประกอบ

นอกจากนี้gotoHashtagยังกำหนดวิธีการเพื่อเลื่อนไปยังองค์ประกอบเมื่อผู้ใช้คลิกที่แท็กจุดยึดโดยเฉพาะ

ปรับปรุง:

หาก url มีสตริงคำค้นหา[routerLink]="['self-route', id]"ใน anchor จะไม่เก็บสตริงเคียวรี ฉันลองทำตามวิธีแก้ปัญหาเหมือนกัน:

<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

อันนี้เวิร์คสำหรับฉัน !! ng นี้เพื่อให้ยึดแท็กแบบไดนามิกคุณต้องรอให้พวกเขาแสดงผล

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>

ไฟล์ 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();
    }
}

อย่าลืมนำเข้าViewChildrenและQueryListอื่น ๆ และเพิ่มตัวสร้างActivatedRoute!!


1

ซึ่งแตกต่างจากคำตอบอื่น ๆ ฉันต้องการนอกจากนี้ยังเพิ่มพร้อมกับfocus() scrollIntoView()นอกจากนี้ฉันกำลังใช้setTimeoutเนื่องจากมันกระโดดขึ้นไปด้านบนไม่เช่นนั้นเมื่อเปลี่ยน URL ไม่แน่ใจว่าอะไรเป็นสาเหตุ แต่ดูเหมือนว่าsetTimeoutจะแก้ปัญหาได้

แหล่งกำเนิดสินค้า:

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

ปลายทาง:

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

typescript:

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

1

ฉันมีปัญหาเดียวกัน วิธีแก้ปัญหา: ใช้ View port Scroller https://angular.io/api/common/ViewportScroller#scrolltoanchor

- รหัส app-Routing.module.ts:

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

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

- องค์ประกอบ HTML

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

- รหัสส่วนประกอบ:

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


    export class ParametrageComponent {

      constructor(private viewScroller: ViewportScroller) {}

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

    }

0

ฉันเพิ่งทดสอบปลั๊กอินที่มีประโยชน์มากใน nmp - ngx-scroll-toซึ่งใช้งานได้ดีสำหรับฉัน อย่างไรก็ตามมันออกแบบมาสำหรับ Angular 4+ แต่อาจมีคนพบว่าคำตอบนี้เป็นประโยชน์


0

ฉันลองใช้วิธีแก้ปัญหาเหล่านี้เกือบทั้งหมด แต่พบปัญหาในการออกและกลับมาพร้อมกับส่วนอื่นมันใช้ไม่ได้ดังนั้นฉันจึงทำสิ่งที่แตกต่างออกไปเล็กน้อยซึ่งใช้งานได้ 100% และกำจัดแฮชที่น่าเกลียดใน URL

tl; dr นี่เป็นวิธีที่ดีกว่าที่ฉันเคยเห็นมา

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();
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.