Angular2使用Hashtag路由到页面锚点

问题描述 投票:98回答:19

我希望在我的Angular2页面上添加一些链接,单击时,将跳转到该页面中的特定位置,就像普通主题标签一样。所以链接会是这样的

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

等等

我不认为我需要HashLocationStrategy,因为我对正常的Angular2方式很好,但是如果我直接添加,链接实际上会跳转到根目录,而不是同一页面上的某个地方。任何方向表示赞赏,谢谢。

routing angular hashtag
19个回答
104
投票

更新

现在支持此功能

<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进行了跟踪


3
投票

我刚在自己的网站上工作,所以我觉得值得在这里发布我的解决方案。

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

然后在您的组件中,确保包含以下内容:

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

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

3
投票

在阅读了所有解决方案之后,我找到了一个组件,我找到了一个完全符合原始问题要求的组件:滚动到锚点链接。 import { ActivatedRoute } from '@angular/router'; constructor(private route: ActivatedRoute) { this.route.fragment.subscribe ( f => { const element = document.querySelector ( "#" + f ) if ( element ) element.scrollIntoView ( element ) }); }

安装时,使用如下语法:

https://www.npmjs.com/package/ng2-scroll-to

它对我来说非常好用。


2
投票

由于fragment属性仍然不提供锚滚动,这个解决方法对我有用:

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

2
投票

适用于没有任何查询参数的页面的简单解决方案是浏览器后退/前进,路由器和深度链接兼容。

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

超时只是允许页面加载由* ngIf“保护”的任何动态数据。这也可用于在更改路线时滚动到页面顶部 - 只提供默认的顶部锚标记。


1
投票

这是另一个解决方法,参考Javier Fuentes的答案:

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

在脚本中:

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

这允许用户直接滚动到元素,如果用户直接落在具有url中的#标签的页面上。

但是在这种情况下,我在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); } } 中订阅了路径片段,但ngAfterViewChecked每个ngAfterViewChecked()都会被连续调用,并且它不允许用户滚动回到顶部,因此在视图滚动到元素后超时0毫秒后调用ngDoCheck

另外,routeFragmentSubscription.unsubscribe方法被定义为在用户专门点击锚标签时滚动到元素。

更新:

如果url有查询字符串,锚点中的qazxsw poi将不会保留查询字符串。我尝试了相同的解决方法:

gotoHashtag

1
投票

这个对我有用!!这个ng为了动态锚定标签,你需要等待它们渲染

HTML:

[routerLink]="['self-route', id]"

我的ts档案:

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

不要忘记导入<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> 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 !!


1
投票

我遇到过同样的问题。解决方案:使用Viewport Scroll qazxsw poi

- app-routing.module.ts代码:

QueryList

- 组件HTML

ActivatedRoute

- 组件代码:

https://angular.io/api/common/ViewportScroller#scrolltoanchor

0
投票

我刚刚在nmp - import { PageComponent } from './page/page.component'; const routes: Routes = [ path: 'page', component: PageComponent }, path: 'page/:id', component: PageComponent } ]; 中测试了非常有用的插件,这对我很有用。不过它是为Angular 4+设计的,但也许有人会觉得这个答案很有帮助。


0
投票

我尝试了大多数这些解决方案,但遇到了问题,离开并返回另一个片段,它不起作用,所以我做了一些有点不同的工作100%,并摆脱URL中的丑陋哈希。

这里的博士比我到目前为止看到的更好。

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

0
投票

与其他答案不同,我还要添加 import { Component } from '@angular/core'; import { ViewportScroller } from '@angular/common'; export class ParametrageComponent { constructor(private viewScroller: ViewportScroller) {} scrollTo(tag : string) { this.viewScroller.scrollToAnchor(tag); } } ngx-scroll-to。此外,我正在使用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(); } } ,因为它在更改URL时跳转到顶部。不知道是什么原因,但似乎focus()做了解决方法。

起源:

scrollIntoView()

目的地:

setTimeout

打字稿:

setTimeout

48
投票

虽然Günter's answer是正确的,但它不包括“跳转到”锚标签部分。

因此,除了:

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

  }
}

请注意,这是一种解决方法!关注this github issue以获取更新。致Victor Savkin提供解决方案!


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

确保在组件的开头导入Router,ActivatedRoute和NavigationEnd,这应该是好的。

Source


18
投票

很抱歉回答迟到了; Angular Routing Documentation中有一个预定义的函数,可帮助我们使用hashtag进行页面锚点路由,即anchorScrolling:'enabled'

步骤1: - 首先在app.module.ts文件中导入RouterModule: -

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: - 通过将ID名称与片段匹配来创建section / div: -

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

作为参考,我通过创建一个有助于解决问题的小型演示添加了以下示例。

但是:ぁzxswい


16
投票

之前的答案都不适合我。在最后的努力中,我尝试了我的模板:

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

在我的.ts:

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

它对预期的内部链接起作用。这实际上并不使用锚标记,因此它根本不会触及URL。


6
投票

以上解决方案对我不起作用......这个做到了:

首先,准备onClick(){ let x = document.querySelector("#foobar"); if (x){ x.scrollIntoView(); } } 在ngAfterViewChecked()中自动滚动...

MyAppComponent

然后,导航到发送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

4
投票

所有其他答案都适用于Angular版本<6.1。但是如果你有最新的版本,那么你就不需要做这些丑陋的黑客,因为Angular已经解决了这个问题。

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

您需要做的就是使用here's the link to issue方法的第二个参数选项设置scrollOffset

RouterModule.forRoot

4
投票

用于@NgModule({ imports: [ RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled', anchorScrolling: 'enabled', scrollOffset: [0, 64] // [x, y] }) ], exports: [RouterModule] }) export class AppRoutingModule {} 中的路由器模块:

app-routing.module.ts

这将在您的HTML中:

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

3
投票

添加到Kalyoyan的<a href="#/users/123#userInfo"> ,此订阅与路由器绑定,并将一直存在,直到页面完全刷新。订阅组件中的路由器事件时,请务必取消订阅ngOnDestroy:

answer
© www.soinside.com 2019 - 2024. All rights reserved.