可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I wish to add some links on my Angular2 page, that when click, will jump to specific positions within that page, like what normal hashtags do. So the links would be something like
/users/123#userInfo
/users/123#userPhoto
/users/123#userLikes
etc.
I don\'t think I need HashLocationStrategy, as I\'m fine with the normal Angular2 way, but if I add directly, the link would actually jump to the root, not somewhere on the same page. Any direction is appreciated, thanks.
回答1:
Update
This is now supported
<a [routerLink]=\"[\'somepath\']\" fragment=\"Test\">Jump to \'Test\' anchor </a>
this._router.navigate( [\'/somepath\', id ], {fragment: \'test\'});
Add Below code to your component to scroll
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
This is a known issue and tracked at https://github.com/angular/angular/issues/6595
回答2:
Although Günter\'s answer is correct, it doesn\'t cover the \"jump to\" the anchor tag part.
Therefore, additionally to:
<a [routerLink]=\"[\'somepath\']\" fragment=\"Test\">Jump to \'Test\' anchor </a>
this._router.navigate( [\'/somepath\', id ], {fragment: \'test\'});
... in the component (parent) where you need a \"jump to\" behavior, add:
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); }
}
}
});
}
}
Please note that this is a workaround! Follow this github issue for future updates. Credits to Victor Savkin for providing the solution!
回答3:
A little late but here\'s an answer I found that works:
<a [routerLink]=\"[\'/path\']\" fragment=\"test\" (click)=\"onAnchorClick()\">Anchor</a>
And in the component:
constructor( private route: ActivatedRoute, private router: Router ) {}
onAnchorClick ( ) {
this.route.fragment.subscribe ( f => {
const element = document.querySelector ( \"#\" + f )
if ( element ) element.scrollIntoView ( element )
});
}
The above doesn\'t automatically scroll to the view if you land on a page with an anchor already, so I used the solution above in my ngInit so that it could work with that as well:
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); }
}
}
});
}
Make sure to import Router, ActivatedRoute and NavigationEnd at the beginning of your component and it should be all good to go.
Source
回答4:
None of the previous answers worked for me. In a last ditch effort, I tried in my template:
<a (click)=\"onClick()\">From Here</a>
<div id=\'foobar\'>To Here</div>
With this in my .ts:
onClick(){
let x = document.querySelector(\"#foobar\");
if (x){
x.scrollIntoView();
}
}
And it works as expected for internal links. This does not actually use anchor tags so it would not touch the URL at all.
回答5:
Solutions above didn\'t work for me... This one did it:
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 } );
}
}
回答6:
Adding on to Kalyoyan\'s answer, this subscription is tied to the router and will live until the page is fully refreshed. When subscribing to router events in a component, be sure to unsubscribe in 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();
}
}
回答7:
I just got this working on my own website, so I figured it would be worth posting my solution here.
<a [routerLink]=\"baseUrlGoesHere\" fragment=\"nameOfYourAnchorGoesHere\">Link Text!</a>
<a name=\"nameOfYourAnchorGoesHere\"></a>
<div>They\'re trying to anchor to me!</div>
And then in your component, make sure you include this:
import { ActivatedRoute } from \'@angular/router\';
constructor(private route: ActivatedRoute) {
this.route.fragment.subscribe ( f => {
const element = document.querySelector ( \"#\" + f )
if ( element ) element.scrollIntoView ( element )
});
}
回答8:
After reading all of the solutions, I looked for a component and I found one which does exactly what the original question asked for: scrolling to anchor links. https://www.npmjs.com/package/ng2-scroll-to
When you install it, you use syntax like:
// 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 {
}
It has worked really well for me.
回答9:
All the other answers will work on Angular version < 6.1. But if you\'ve got the latest version then you won\'t need to do these ugly hacks as Angular has fixed the issue.
here\'s the link to issue
All you\'d need to do is set scrollOffset
with the option of the second argument ofRouterModule.forRoot
method.
@NgModule({
imports: [
RouterModule.forRoot(routes, {
scrollPositionRestoration: \'enabled\',
anchorScrolling: \'enabled\',
scrollOffset: [0, 64] // [x, y]
})
],
exports: [RouterModule]
})
export class AppRoutingModule {}
回答10:
Since the fragment property still doesn\'t provide anchor scrolling, this workaround did the trick for me:
<div [routerLink]=\"[\'somepath\']\" fragment=\"Test\">
<a href=\"#Test\">Jump to \'Test\' anchor </a>
</div>
回答11:
A simple solution that works for pages without any query parameters, is browser back / forward, router and deep-linking compliant.
<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();
}
}
The timeout is simply to allow the page to load any dynamic data \"protected\" by an *ngIf. This can also be used to scroll to the top of the page when changing route - just provide a default top anchor tag.
回答12:
Sorry for answering it bit late; There is a pre-defined function in the Angular Routing Documentation which helps us in routing with a hashtag to page anchor i.e, anchorScrolling: \'enabled\'
Step-1:- First Import the RouterModule in the app.module.ts file:-
imports:[
BrowserModule,
FormsModule,
RouterModule.forRoot(routes,{
anchorScrolling: \'enabled\'
})
],
Step-2:- Go to the HTML Page, Create the navigation and add two important attributes like [routerLink] and fragment for matching the respective Div ID\'s:-
<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>
Step-3:- Create a section/div by matching the ID name with the 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>
For your reference, I have added the example below by creating a small demo which helps to solve your problem.
Demo : https://routing-hashtag-page-anchors.stackblitz.io/
回答13:
Here is another workaround with refernce to JavierFuentes answer:
<a [routerLink]=\"[\'self-route\', id]\" fragment=\"some-element\" (click)=\"gotoHashtag(\'some-element\')\">Jump to Element</a>
in script:
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);
}
}
This allows user to directly scroll to element, if user directly lands on the page having hashtag in url.
But in this case, I have subscribed route Fragment in ngAfterViewChecked
but ngAfterViewChecked()
gets called continuously per every ngDoCheck
and it doesn\'t allows user to scroll back to top, so routeFragmentSubscription.unsubscribe
is called after a timeout of 0 millis after view is scrolled to element.
Additionally gotoHashtag
method is defined to scroll to element when user specifically clicks on the anchor tag.
Update:
If url has querystrings, [routerLink]=\"[\'self-route\', id]\"
in anchor wont preserve the querystrings. I tried following workaround for the same:
<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);
}
回答14:
I\'ve just tested 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.
回答15:
I tried most of these solutions but ran into problems leaving and coming back with another fragment it wouldn\'t work, so I did something a bit different that works 100%, and gets rid of the ugly hash in the URL.
tl;dr here\'s a better way than what I\'ve seen so far.
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();
}
}
回答16:
I had the same issue.
The solution : using View port Scroller https://angular.io/api/common/ViewportScroller#scrolltoanchor
<a (click) = \"scrollTo(\'typeExec\')\">
<mat-icon>lens</mat-icon>
</a>
-- Component\'s code :
export class ParametrageComponent {
constructor(private viewScroller: ViewportScroller) {}
scrollTo(tag : string)
{
this.viewScroller.scrollToAnchor(tag);
}
}
回答17:
This one work for me !! This ngFor so it dynamically anchor tag, You need to wait them render
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>
My ts file:
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();
}
}
Don\'t forget to import that ViewChildren
, QueryList
etc.. and add some constructor ActivatedRoute
!!
回答18:
Unlike other answers I\'d additionally also add focus()
along with scrollIntoView()
.
Also I\'m using setTimeout
since it jumps to top otherwise when changing the URL. Not sure what was the reason for that but it seems setTimeout
does the workaround.
Origin:
<a [routerLink] fragment=\"some-id\" (click)=\"scrollIntoView(\'some-id\')\">Jump</a>
Destination:
<a id=\"some-id\" tabindex=\"-1\"></a>
Typescript:
scrollIntoView(anchorHash) {
setTimeout(() => {
const anchor = document.getElementById(anchorHash);
if (anchor) {
anchor.focus();
anchor.scrollIntoView();
}
});
}
回答19:
Use this for the router module in app-routing.module.ts
:
@NgModule({
imports: [RouterModule.forRoot(routes, {
useHash: true,
scrollPositionRestoration: \'enabled\',
anchorScrolling: \'enabled\',
scrollOffset: [0, 64]
})],
exports: [RouterModule]
})
This will be in your HTML:
<a href=\"#/users/123#userInfo\">