12

So I used the following post to figure out how to add a fragment to the url.

Angular2 Routing with Hashtag to page anchor

And the fragment gets added fine, however the page doesn't move to the resulting anchor. I have tried using both the name and id attribute on the destination div, but neither seem to work.

I'm using Angular Version 2.0.0-rc.3, and router version 3.0.0-alpha.6.

Thanks!

<a [routerLink]="[]" fragment="destination">Go TO DIV</a>

<div id='destination'>
    <h2>Destination</h2>
</div>

There is enough space between the 2 elements to tell if the page actually moves.

And as said before, the url correctly adds #destination to itself.

Community
  • 1
  • 1
jipson
  • 79
  • 1
  • 3
  • 14

7 Answers7

15

A workaround might be to write a function in your component that sets the location.hash to the id of the div you want to navigate to.

<a (click)="goTo('destination')">Go TO DIV</a>

<div id='destination'>
    <h2>Destination</h2>
</div>

Then in your component:

goTo(location: string): void {
    window.location.hash = location;
}
JohnMAC
  • 166
  • 1
  • 3
  • 5
    I would add one note to this. It sets hash only once, so if the hash is already included in the page and you click on the same anchor link, it won't scroll to the destination. But it works if you clear the hash before setting -> `window.location.hash = ''; window.location.hash = location;` – Samurai Girl Apr 23 '17 at 20:47
  • I tried this, it works, but then everything above the target disappears. – JonathanPeel May 02 '17 at 14:06
  • 1
    Far less complicated than using the router. I understand why this is complicated because of routing and all that SPA stuff, but anchor links are such a basic feature. – Gilles Dec 29 '17 at 02:16
3

Here's a very simple solution that worked for me:

In the component, add this method:

navigateTo(location: string): void {
// location will be a valid CSS ID selector; i.e. it should be preceded with '#'
window.location.hash = location;
setTimeout(() => {
    document.querySelector(location).parentElement.scrollIntoView();
  });
}

And in the view, the <a> tag will look like this:

<a (click)="navigateTo('#destination')">Destination</a>
Soma Mbadiwe
  • 1,594
  • 16
  • 15
  • Currently using Angular 4 / CLI. This was the BEST solution. Thank you! Also, simplest with smallest amount of code that actually worked. – Mindsect Team Oct 14 '17 at 22:44
2

This worked for me:

First, prepare MyAppComponent for automatic scrolling 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 );
              }
            }
          } );
    }

  }

}

Then, navigate to my-app-route sending 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 } );
  }

}
JavierFuentes
  • 1,840
  • 18
  • 13
2

This workaround should do the trick

<div [routerLink]="[]" fragment="destination">
  <a href="#destination">Go TO DIV</a>
</div>
Martin Cremer
  • 5,191
  • 2
  • 32
  • 38
2

After seeing all the solutions here, I decided to go with a small and simple directive. Here's what I ended up with:

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({ selector: '[appAnchorTo]' })
export class AnchorToDirective {
  @Input() target: string;

  constructor(el: ElementRef) { el.nativeElement.style.cursor = 'pointer'; }

  @HostListener('click', ['$event'])
  onClick() { document.querySelector(this.target).scrollIntoView(); }
}

The idea was to create something flexible, that could be attached to any element on the page.

Usage:

<a appAnchorTo target="#my-link">My Insights</a>
<div appAnchorTo target="#my-other-link">My Customers</div>

And don't forget to declare the directive on one of your Modules:

@NgModule({
    ...,
    declarations: [
        ...,
        AnchorToDirective,
    ]
})
rafaelbiten
  • 6,074
  • 2
  • 31
  • 36
1

You can also use this plugin https://www.npmjs.com/package/ng2-page-scroll It does an animated scroll to a given anchor.

0

I've found very useful plugin available in nmp - ngx-scroll-to, which works great for me. However it's designed for Angular 4+, but maybe somebody will find this answer helpful.

mpro
  • 14,302
  • 5
  • 28
  • 43