2016-03-19 2 views
51

Я хочу добавить некоторые ссылки на мою страницу Angular2, чтобы при щелчке переместился в определенные позиции в пределах этой страницы, как то, что делают обычные хэштеги. Таким образом, ссылки будут что-то вродеAngular2 Маршрутизация с помощью Hashtag на привязку к странице

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

т.д.

Я не думаю, что мне нужно HashLocationStrategy, так как я хорошо с обычным способом Angular2, но если добавить непосредственно, ссылка будет на самом деле переходите к корню, а не где-то на той же странице. Любое направление оценивается, спасибо.

ответ

58

Update

Это теперь поддерживается

<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

+0

что означает 'id'? – invot

+1

@invot переменная со строкой (например, вопрос «123» в вопросе), предполагая, что маршрут пути ожидает такой параметр, как «{path:« users /: id », ....}' –

+0

Что делать, если я хотите, чтобы #anchor отображался только в том случае, если фрагмент не равен null/undefined? В настоящее время у меня все еще есть http://somethi.ng/page#. Если нет якоря, это должно быть просто http://somethi.ng/page. Если есть якорь, он все равно должен быть http://somethi.ng/page#anchor. – Kunepro

27

Хотя 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 за предоставленные услуги!

+0

он работает. спасибо – ruucm

+0

У меня нет сомнений. –

+1

он работает, вы спасли мой день, спасибо! – Torai

11

Немного поздно, но вот ответ, который я обнаружил, что работает:

<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

+0

Работает для меня! Если вы хотите перемещаться по той же странице, на которой вы уже находитесь, используйте [routerLink] = "['.']" – Raoul

2

выше решения не работает для меня ...Это один сделал это:

Во-первых, подготовить 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 }); 
    } 

} 
+0

Вниз голосование без комментариев или объяснений ... это не очень хорошая практика ... – JavierFuentes

+0

Спасибо за На голосование, значит, это сработало и для вас ... это решение действительно спасло мой день! – JavierFuentes

+0

Это сработало для меня! Зачем использовать ngAfterViewChecked вместо ngInit? –

0

Вот еще обходной путь с refernce к 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); 
     } 
} 

Это позволяет пользователю непосредственно перейти к элементу, если пользователь непосредственно попадает на страницу, имеющей хэштегом в ссылке.

Но в этом случае, я подписался маршрут фрагмент в ngAfterViewChecked но ngAfterViewChecked() получает постоянно называют в каждом ngDoCheck и это не позволяет пользователю прокручивать назад к вершине, так routeFragmentSubscription.unsubscribe вызывается после тайм-аута 0 Millis после мнение прокручивается до элемента.

Кроме того, метод gotoHashtag предназначен для прокрутки к элементу, когда пользователь специально нажимает на тег привязки.

Update:

Если URL имеет querystrings, [routerLink]="['self-route', id]" анкер обыкновение сохранять querystrings. Я попытался следующий обходной путь для того же:

<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

Поскольку свойство фрагмент еще не обеспечивает якорь скроллинг, этот способ сделал трюк для меня:

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

Добавление к этой подписки Kalyoyan в answer привязан к маршрутизатору и будет жить до тех пор, пока страница не будет полностью обновлена. При подключении к маршрутизатору события в компоненте, обязательно отписываться в 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

Я только что получил эту работу на свой сайт, так что я решил, что это стоило бы разместить мое решение здесь.

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

Я думаю, что лучше написать просто 'element.scrollIntoView()' или 'element.scrollIntoView (true)'. Ваша версия не скомпилировалась для меня (возможно, из-за строгихNullChecks?). –

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

Он работал очень хорошо для меня.

+0

Используйте колесо, не изобретайте его снова;) –

+0

Вы посмотрели код, стоящий за этим компонентом?Он выглядит очень хрупким - в проекте также есть 14 открытых проблем, которые включают в себя такие вещи, как элемент, который не существует, задает нуль, а не прокручивает элемент, проблемы с поддержкой браузера. – Ryan

3

Ни один из предыдущих ответов не работал для меня.В последнем усилии рва, я пытался в моем шаблоне:

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

С этим в моих .ts:

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

И это работает, как ожидается, для внутренних ссылок. На самом деле это не использует теги привязки, чтобы он вообще не касался URL.

0

Простое решение, которое работает для страниц без каких-либо параметров запроса, совместимо с браузером/переходом, маршрутизатором и глубокой связью.

<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. Это также можно использовать для прокрутки до верхней части страницы при смене маршрута - просто укажите основной якорный тег по умолчанию.

0

Я только что проверил очень полезный плагин, доступный в nmp - ngx-scroll-to, который отлично работает для меня. Однако он предназначен для Angular 4+, но, возможно, кто-то найдет этот ответ полезным.

Смежные вопросы