router教學 - Angular 2滾動到路線更改頂部




angular2 router教學 (14)

@Fernando Echeverria太好了! 但是此代碼不適用於哈希路由器或惰性路由器。 因為它們不會觸發位置更改。 可以嘗試一下:

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

@Directive({
    selector: '[linkToTop]'
})
export class LinkToTopDirective {

    @HostListener('click')
    onClick(): void {
        window.scrollTo(0, 0);
    }
}

在我的Angular 2應用程序中,當我向下滾動頁面並單擊頁面底部的鏈接時,它確實會更改路線並帶我進入下一頁,但不會滾動到頁面頂部。 結果,如果第一頁很長而第二頁的內容很少,則給人的印像是第二頁缺少內容。 由於僅當用戶滾動到頁面頂部時內容才可見。

我可以在組件的ngInit中將窗口滾動到頁面頂部,但是,有沒有更好的解決方案可以自動處理應用程序中的所有路由?


Angular最近引入了一項新功能,內部的角路由模塊進行如下更改

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

只需點擊一下即可輕鬆完成

在您的主要組件html中創建參考#scrollContainer

<div class="main-container" #scrollContainer>
    <router-outlet (activate)="onActivate($event, scrollContainer)"></router-outlet>
</div>

在主要組件.ts中

onActivate(e, scrollContainer) {
    scrollContainer.scrollTop = 0;
}

嗨,大家好,這對我來說適用於角度4。您只需參考父級即可滾動更改路由器

layout.component.pug

 html {
      scroll-behavior: smooth;
    }

layout.component.ts

private lastRouteUrl: string[] = []
  

ngOnInit(): void {
  this.router.events.subscribe((ev) => {
    const len = this.lastRouteUrl.length
    if (ev instanceof NavigationEnd) {
      this.lastRouteUrl.push(ev.url)
      if (len > 1 && ev.url === this.lastRouteUrl[len - 2]) {
        return
      }
      window.scrollTo(0, 0)
    }
  })
}

如果您只需要滾動頁面到頂部,則可以執行此操作(不是最佳解決方案,但是速度很快)

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

對於iPhone / iOS Safari,您可以使用setTimeout包裝

 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 6.1開始,路由器提供了一個名為 scrollPositionRestoration 配置選項 ,旨在滿足這種情況。

export class AppComponent implements OnInit {
  isPopState = false;

  constructor(private router: Router, private locStrat: LocationStrategy) { }

  ngOnInit(): void {
    this.locStrat.onPopState(() => {
      this.isPopState = true;
    });

    this.router.events.subscribe(event => {
      // Scroll to top if accessing a page, not via browser history stack
      if (event instanceof NavigationEnd && !this.isPopState) {
        window.scrollTo(0, 0);
        this.isPopState = false;
      }

      // Ensures that isPopState is reset
      if (event instanceof NavigationEnd) {
        this.isPopState = false;
      }
    });
  }
}

您可以利用可觀察的 filter 方法來更簡潔地編寫此代碼:

this.router.events.filter(event => event instanceof NavigationEnd).subscribe(() => {
      this.window.scrollTo(0, 0);
});

如果在使用Angular Material 2 sidenav時滾動到頂部時遇到問題,這將有所幫助。 窗口或文檔主體沒有滾動條,因此您需要獲取 sidenav 內容容器並滾動該元素,否則請嘗試將窗口默認滾動。

this.router.events.filter(event => event instanceof NavigationEnd)
  .subscribe(() => {
      const contentContainer = document.querySelector('.mat-sidenav-content') || this.window;
      contentContainer.scrollTo(0, 0);
});

另外,Angular CDK v6.x現在具有 滾動包 ,可能有助於處理滾動。


您可以將 AfterViewInit 生命週期掛鉤添加到您的組件。

ngAfterViewInit() {
   window.scrollTo(0, 0);
}

最好的答案是在Angular GitHub討論中( 更改路線不會滾動到新頁面的頂部 )。

也許您只想在根路由器更改中排在首位(而不是在子級中,因為您可以在選項卡集中的延遲加載中加載路由)

app.component.html

<router-outlet (deactivate)="onDeactivate()"></router-outlet>

app.component.ts

onDeactivate() {
  document.body.scrollTop = 0;
  // Alternatively, you can scroll to top by using this other call:
  // window.scrollTo(0, 0)
}

完全歸功於 JoniJnm 原始帖子


該解決方案基於@FernandoEcheverria和@GuilhermeMeireles的解決方案,但更為簡潔,並且可以與Angular Router提供的popstate機制一起使用。 這允許存儲和恢復多個連續導航的滾動級別。

我們將每個導航狀態的滾動位置存儲在地圖 scrollLevels 。 一旦出現popstate事件,Angular Router將提供將要還原的狀態的ID: event.restoredState.navigationId 。 然後將其用於從 scrollLevels 獲取該狀態的最後一個滾動級別。

如果該路線沒有存儲的滾動級別,它將按照您的期望滾動到頂部。

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

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

  constructor(private router: Router) { }

  ngOnInit() {
    const scrollLevels: { [navigationId: number]: number } = {};
    let lastId = 0;
    let restoredId: number;

    this.router.events.subscribe((event: Event) => {

      if (event instanceof NavigationStart) {
        scrollLevels[lastId] = window.scrollY;
        lastId = event.id;
        restoredId = event.restoredState ? event.restoredState.navigationId : undefined;
      }

      if (event instanceof NavigationEnd) {
        if (restoredId) {
          // Optional: Wrap a timeout around the next line to wait for
          // the component to finish loading
          window.scrollTo(0, scrollLevels[restoredId] || 0);
        } else {
          window.scrollTo(0, 0);
        }
      }

    });
  }

}

這對所有導航更改(包括哈希導航)最適合我

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Location, PopStateEvent } from '@angular/common';
import { Router, Route, RouterLink, NavigationStart, NavigationEnd, 
    RouterEvent } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';

@Component({
  selector: 'my-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {

  private _subscription: Subscription;
  private _scrollHistory: { url: string, y: number }[] = [];
  private _useHistory = false;

  constructor(
    private _router: Router,
    private _location: Location) {
  }

  public ngOnInit() {

    this._subscription = this._router.events.subscribe((event: any) => 
    {
      if (event instanceof NavigationStart) {
        const currentUrl = (this._location.path() !== '') 
           this._location.path() : '/';
        const item = this._scrollHistory.find(x => x.url === currentUrl);
        if (item) {
          item.y = window.scrollY;
        } else {
          this._scrollHistory.push({ url: currentUrl, y: window.scrollY });
        }
        return;
      }
      if (event instanceof NavigationEnd) {
        if (this._useHistory) {
          this._useHistory = false;
          window.scrollTo(0, this._scrollHistory.find(x => x.url === 
          event.url).y);
        } else {
          window.scrollTo(0, 0);
        }
      }
    });

    this._subscription.add(this._location.subscribe((event: PopStateEvent) 
      => { this._useHistory = true;
    }));
  }

  public ngOnDestroy(): void {
    this._subscription.unsubscribe();
  }
}

除瞭如下所示的 提供的完美答案外,您還可以通過添加平滑滾動來調整實現,如下所示

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

然後在下面添加代碼段

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

到你的styles.css


window.scrollTo() 在Angular 5中對我不起作用,所以我使用了 document.body.scrollTop 類的東西,

 this.router.events.subscribe((evt) => { if (evt instanceof NavigationEnd) { document.body.scrollTop = 0; } }); 




angular2-directives