在我的Angular 2应用中,当我向下滚动页面并单击页面底部的链接时,它确实会改变路由并将我带到下一页,但它不会滚动到页面顶部。因此,如果第一页很长,第二页内容很少,就会给人一种第二页缺乏内容的印象。因为只有当用户滚动到页面顶部时,内容才可见。

我可以滚动窗口到ngInit组件的页面顶部,但是,有没有更好的解决方案,可以自动处理我的应用程序中的所有路由?


当前回答

Angular 6.1及更高版本:

Angular 6.1(发布于2018-07-25)通过一个名为“路由器滚动位置恢复”的特性,添加了内置支持来处理这个问题。正如Angular官方博客中所描述的,你只需要在路由器配置中启用它,如下所示:

RouterModule.forRoot(routes, {scrollPositionRestoration: 'enabled'})

此外,该博客称“预计这将成为未来主要版本的默认设置”。到目前为止,这种情况还没有发生(从Angular 11.0开始),但最终你将不需要在代码中做任何事情,它将会正确地开箱工作。

你可以在官方文档中看到更多关于这个特性以及如何定制这个行为的细节。

Angular 6.0及更早版本:

虽然@GuilhermeMeireles的出色回答解决了最初的问题,但它引入了一个新问题,打破了向后或向前导航(使用浏览器按钮或通过代码中的位置)时预期的正常行为。预期的行为是,当您导航回页面时,它应该保持向下滚动到与单击链接时相同的位置,但在到达每个页面时滚动到顶部显然打破了这一预期。

下面的代码扩展了检测这种导航的逻辑,方法是订阅Location的PopStateEvent序列,如果新到达的页面是这种事件的结果,则跳过滚动到顶部的逻辑。

如果你返回的页面足够长,足以覆盖整个视口,滚动位置将自动恢复,但正如@JordanNelson正确指出的那样,如果页面较短,你需要跟踪原始的y轴滚动位置,并在返回页面时显式地恢复它。代码的更新版本也涵盖了这种情况,总是显式地恢复滚动位置。

import { Component, OnInit } from '@angular/core';
import { Router, NavigationStart, NavigationEnd } from '@angular/router';
import { Location, PopStateEvent } from "@angular/common";

@Component({
    selector: 'my-app',
    template: '<ng-content></ng-content>',
})
export class MyAppComponent implements OnInit {

    private lastPoppedUrl: string;
    private yScrollStack: number[] = [];

    constructor(private router: Router, private location: Location) { }

    ngOnInit() {
        this.location.subscribe((ev:PopStateEvent) => {
            this.lastPoppedUrl = ev.url;
        });
        this.router.events.subscribe((ev:any) => {
            if (ev instanceof NavigationStart) {
                if (ev.url != this.lastPoppedUrl)
                    this.yScrollStack.push(window.scrollY);
            } else if (ev instanceof NavigationEnd) {
                if (ev.url == this.lastPoppedUrl) {
                    this.lastPoppedUrl = undefined;
                    window.scrollTo(0, this.yScrollStack.pop());
                } else
                    window.scrollTo(0, 0);
            }
        });
    }
}

其他回答

对于每个正在寻找解决方案并阅读这篇文章的人。的

imports: [
  RouterModule.forRoot(routes, {
    scrollPositionRestoration: 'enabled'
  }),
  ...
]

并没有回答这个主题的问题。如果我们查看Angular的源代码,我们会看到一些有趣的行:

这个东西只对反向导航有用。其中一个解决方案可能是:

constructor(router: Router) {

    router.events
        .pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd))
        .subscribe(() => {
            this.document.querySelector('#top').scrollIntoView();
        });
}

这将查看每个导航到该id的div并滚动到它;

另一种方法是使用相同的逻辑,但在decorator或指令的帮助下,允许你选择何时何地滚动顶部;

对于iphone/ios safari,您可以使用setTimeout进行包装

setTimeout(function(){
    window.scrollTo(0, 1);
}, 0);

从Angular 6.1开始,路由器提供了一个名为scrollPositionRestoration的配置选项,这是为了满足这种情况而设计的。

imports: [
  RouterModule.forRoot(routes, {
    scrollPositionRestoration: 'enabled'
  }),
  ...
]

您可以在主组件上注册路由更改侦听器,并在路由更改时滚动到顶部。

import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';

@Component({
    selector: 'my-app',
    template: '<ng-content></ng-content>',
})
export class MyAppComponent implements OnInit {
    constructor(private router: Router) { }

    ngOnInit() {
        this.router.events.subscribe((evt) => {
            if (!(evt instanceof NavigationEnd)) {
                return;
            }
            window.scrollTo(0, 0)
        });
    }
}

大家好,这在angular 4中是适用的。你只需要引用父节点来滚动路由器更改

layout.component.pug

.wrapper(#outlet="")
    router-outlet((activate)='routerActivate($event,outlet)')

layout.component.ts

 public routerActivate(event,outlet){
    outlet.scrollTop = 0;
 }`