Angular 5 Gulir ke atas pada setiap klik Rute

104

Saya menggunakan sudut 5. Saya memiliki dasbor di mana saya memiliki beberapa bagian dengan konten kecil dan beberapa bagian dengan konten yang sangat besar sehingga saya menghadapi masalah saat mengganti router saat pergi ke atas. Setiap kali saya perlu menggulir untuk pergi ke atas. Adakah yang bisa membantu saya untuk menyelesaikan masalah ini sehingga ketika saya mengubah router, pandangan saya selalu berada di atas.

Terima kasih sebelumnya.

raihan
sumber
Kemungkinan duplikat Angular 2 Gulir ke atas pada Perubahan Rute
Cerbrus

Jawaban:

218

Ada beberapa solusi, pastikan untuk memeriksa semuanya :)


Outlet router akan memancarkan activateacara setiap kali komponen baru dibuat, jadi kita dapat menggunakan (activate)untuk menggulir (misalnya) ke atas:

app.component.html

<router-outlet (activate)="onActivate($event)" ></router-outlet>

app.component.ts

onActivate(event) {
    window.scroll(0,0);
    //or document.body.scrollTop = 0;
    //or document.querySelector('body').scrollTo(0,0)
    ...
}

Gunakan, misalnya, solusi ini untuk pengguliran yang mulus:

    onActivate(event) {
        let scrollToTop = window.setInterval(() => {
            let pos = window.pageYOffset;
            if (pos > 0) {
                window.scrollTo(0, pos - 20); // how far to scroll on each step
            } else {
                window.clearInterval(scrollToTop);
            }
        }, 16);
    }

Jika Anda ingin selektif, katakanlah tidak setiap komponen harus memicu scrolling, Anda dapat memeriksanya dalam ifpernyataan seperti ini:

onActivate(e) {
    if (e.constructor.name)==="login"{ // for example
            window.scroll(0,0);
    }
}

Sejak Angular6.1, kita juga dapat menggunakan { scrollPositionRestoration: 'enabled' }modul yang dimuat dengan penuh semangat dan ini akan diterapkan ke semua rute:

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

Ini juga akan melakukan pengguliran halus. Namun hal ini menimbulkan ketidaknyamanan untuk melakukannya di setiap perutean.


Solusi lain adalah melakukan scrolling atas pada animasi router. Tambahkan ini di setiap transisi di mana Anda ingin menggulir ke atas:

query(':enter, :leave', style({ position: 'fixed' }), { optional: true }) 
Vega
sumber
4
bekerja . Terima kasih atas jawaban anda. Anda menghemat banyak waktu untuk saya
raihan
2
acara gulir pada windowobjek tidak bekerja dalam sudut 5. Ada tebakan mengapa?
Sahil Babbar
2
Pahlawan sejati di sini. Menghemat kemungkinan berjam-jam frustrasi. Terima kasih!
Anjana Silva
1
@AnjanaSilva, terima kasih atas komentar positifnya! Saya sangat senang ini dapat membantu Anda :)
Vega
2
Apakah ada cara untuk modul lazyloaded?
Pankaj Prakash
54

Jika Anda menghadapi masalah ini di Angular 6, Anda dapat memperbaikinya dengan menambahkan parameter scrollPositionRestoration: 'enabled'ke RouterModule app-routing.module.ts:

@NgModule({
  imports: [RouterModule.forRoot(routes,{
    scrollPositionRestoration: 'enabled'
  })],
  exports: [RouterModule]
})
Nimezzz
sumber
7
Harap jangan memposting gambar kode. Cukup salin dan tempel kode itu sendiri langsung ke jawaban Anda.
mypetlion
2
Tentu. Lain kali aku akan melakukannya. Aku baru di sini. :)
Nimezzz
4
Perhatikan bahwa mulai 6-Nov-2019 menggunakan Angular 8, setidaknya, scrollPositionRestorationproperti tidak berfungsi dengan konten halaman dinamis (yaitu, di mana konten halaman dimuat secara asinkron): lihat laporan bug Angular ini: github.com/angular/angular / issues / 24547
dbeachy1
25

EDIT: Untuk Angular 6+, mohon gunakan jawaban Nimesh Nishara Indimagedara yang menyebutkan:

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

Jawaban Asli:

Jika semua gagal, buat beberapa elemen HTML kosong (mis .: div) di bagian atas (atau scroll yang diinginkan ke lokasi) dengan id = "top" pada template (atau template induk):

<div id="top"></div>

Dan dalam komponen:

  ngAfterViewInit() {
    // Hack: Scrolls to top of Page after page view initialized
    let top = document.getElementById('top');
    if (top !== null) {
      top.scrollIntoView();
      top = null;
    }
  }
GeoRover
sumber
2
Solusi ini berfungsi untuk saya (diuji di Chrome serta Edge). Solusi yang diterima tidak berhasil untuk proyek saya (Angular5)
Rob van Meeuwen
@RobvanMeeuwen, Jika jawaban saya tidak berhasil, mungkin Anda tidak menerapkannya dengan cara yang sama. Solusi ini memanipulasi secara langsung DOM yang tidak benar juga aman
Vega
@Vega, itulah mengapa saya menyebutnya hack. Solusi Anda adalah solusi yang tepat. Beberapa orang di sini tidak dapat menerapkan milik Anda, jadi saya menawarkan peretasan fallback. Mereka harus merefaktor kode mereka berdasarkan versi yang mereka gunakan saat ini.
GeoRover
Dari semua solusi ini berhasil untuk saya. Terima kasih @GeoRover
Roshan
Untuk Angular 6+, mohon gunakan jawaban Nimesh Nishara Indimagedara.
GeoRover
6

Meskipun @Vega memberikan jawaban langsung atas pertanyaan Anda, ada beberapa masalah. Ini merusak tombol kembali / maju browser. Jika Anda pengguna mengklik tombol kembali atau maju browser, mereka kehilangan tempatnya dan digulirkan ke atas. Ini bisa sedikit menyebalkan bagi pengguna Anda jika mereka harus menggulir ke bawah untuk mendapatkan tautan dan memutuskan untuk mengeklik kembali hanya untuk menemukan bilah gulir telah disetel ulang ke atas.

Inilah solusi saya untuk masalah tersebut.

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;
      }
    });
  }
}
Sal_Vader_808
sumber
2
Terima kasih atas kode lanjutan dan solusi bagus. Tapi terkadang solusi @Vega lebih baik, karena mengatasi banyak masalah dengan animasi dan tinggi halaman dinamis. Solusi Anda bagus jika Anda memiliki halaman panjang dengan konten dan animasi perutean sederhana. Saya mencobanya di halaman dengan banyak blok animasi dan dinamika dan tampilannya tidak begitu bagus. Saya pikir terkadang kita bisa mengorbankan 'posisi belakang' untuk aplikasi kita. Tetapi jika tidak - solusi Anda adalah yang terbaik yang saya lihat untuk Angular. Sekali lagi terima kasih
Denis Savenko
5

Dalam kasus saya, saya baru saja menambahkan

window.scroll(0,0);

di ngOnInit()dan halus bekerja.

Zohab Ali
sumber
4

Dari Angular Versi 6+ Tidak perlu menggunakan window.scroll (0,0)

Untuk versi Angular 6+dari @ Mewakili opsi untuk mengkonfigurasi router.docs

interface ExtraOptions {
  enableTracing?: boolean
  useHash?: boolean
  initialNavigation?: InitialNavigation
  errorHandler?: ErrorHandler
  preloadingStrategy?: any
  onSameUrlNavigation?: 'reload' | 'ignore'
  scrollPositionRestoration?: 'disabled' | 'enabled' | 'top'
  anchorScrolling?: 'disabled' | 'enabled'
  scrollOffset?: [number, number] | (() => [number, number])
  paramsInheritanceStrategy?: 'emptyOnly' | 'always'
  malformedUriErrorHandler?: (error: URIError, urlSerializer: UrlSerializer, url: string) => UrlTree
  urlUpdateStrategy?: 'deferred' | 'eager'
  relativeLinkResolution?: 'legacy' | 'corrected'
}

Dapat digunakan scrollPositionRestoration?: 'disabled' | 'enabled' | 'top'dalam

Contoh:

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

Dan jika seseorang perlu untuk mengontrol pengguliran secara manual, Tidak perlu menggunakan window.scroll(0,0) Sebaliknya dari paket umum Angular V6 telah diperkenalkan ViewPortScoller.

abstract class ViewportScroller {
  static ngInjectableDef: defineInjectable({ providedIn: 'root', factory: () => new BrowserViewportScroller(inject(DOCUMENT), window) })
  abstract setOffset(offset: [number, number] | (() => [number, number])): void
  abstract getScrollPosition(): [number, number]
  abstract scrollToPosition(position: [number, number]): void
  abstract scrollToAnchor(anchor: string): void
  abstract setHistoryScrollRestoration(scrollRestoration: 'auto' | 'manual'): void
}

Penggunaan cukup mudah Contoh:

import { Router } from '@angular/router';
import {  ViewportScroller } from '@angular/common'; //import
export class RouteService {

  private applicationInitialRoutes: Routes;
  constructor(
    private router: Router;
    private viewPortScroller: ViewportScroller//inject
  )
  {
   this.router.events.pipe(
            filter(event => event instanceof NavigationEnd))
            .subscribe(() => this.viewPortScroller.scrollToPosition([0, 0]));
}
Vikas
sumber
4

jika Anda menggunakan mat-sidenav memberikan id ke outlet router (jika Anda memiliki outlet router orang tua dan anak) dan menggunakan fungsi aktifkan di dalamnya <router-outlet id="main-content" (activate)="onActivate($event)"> dan gunakan pemilih kueri 'mat-sidenav-content' ini untuk menggulir ke atas onActivate(event) { document.querySelector("mat-sidenav-content").scrollTo(0, 0); }

ivin antony
sumber
Berfungsi dengan baik bahkan tanpa menggunakan id (Saya punya satu router-outletdi aplikasi saya). Saya juga melakukannya dengan lebih 'sudut': @ViewChild(MatSidenavContainer) sidenavContainer: MatSidenavContainer; onActivate() { this.sidenavContainer.scrollable.scrollTo({ left: 0, top: 0 }); }
GCSDC
3

Saya terus mencari solusi bawaan untuk masalah ini seperti yang ada di AngularJS. Tetapi sampai saat itu solusi ini berfungsi untuk saya, Sederhana, dan mempertahankan fungsionalitas tombol kembali.

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

Jawaban dari zurfyx postingan asli

Jared Whipple
sumber
3

Anda hanya perlu membuat fungsi yang berisi penyesuaian scrolling layar Anda

sebagai contoh

window.scroll(0,0) OR window.scrollTo() by passing appropriate parameter.

window.scrollTo (xpos, ypos) -> parameter yang diharapkan.

Vijay Barot
sumber
2

Berikut adalah solusi yang hanya menggulir ke atas Komponen jika pertama kali mengunjungi komponen SETIAP (jika Anda perlu melakukan sesuatu yang berbeda per komponen):

Di setiap Komponen:

export class MyComponent implements OnInit {

firstLoad: boolean = true;

...

ngOnInit() {

  if(this.firstLoad) {
    window.scroll(0,0);
    this.firstLoad = false;
  }
  ...
}
0_tr0jan_0
sumber
2

export class AppComponent {
  constructor(private router: Router) {
    router.events.subscribe((val) => {
      if (val instanceof NavigationEnd) {
        window.scrollTo(0, 0);
      }
    });
  }

}

irorika
sumber
2

Angular 6.1 dan yang lebih baru:

Anda dapat menggunakan solusi bawaan yang tersedia di Angular 6.1+ dengan opsi scrollPositionRestoration: 'enabled'untuk mencapai hal yang sama.

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

Angular 6.0 dan sebelumnya:

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

Catatan: Perilaku yang diharapkan adalah ketika Anda menavigasi kembali ke halaman, itu harus tetap di-scroll ke bawah ke lokasi yang sama seperti saat Anda mengklik link, tetapi scrolling ke atas saat membuka setiap halaman.

s sharif
sumber
2

tambahkan saja

window.scrollTo({ top: 0);

ke ngOnInit ()

Max Hesari
sumber
window.scroll (0,0)
Akitha_MJ
2

Untuk beberapa orang yang mencari fungsi gulir, cukup tambahkan fungsi dan panggil bila diperlukan

scrollbarTop(){

  window.scroll(0,0);
}
Akitha_MJ
sumber
1

Coba ini:

app.component.ts

import {Component, OnInit, OnDestroy} from '@angular/core';
import {Router, NavigationEnd} from '@angular/router';
import {filter} from 'rxjs/operators';
import {Subscription} from 'rxjs';

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

    constructor(private router: Router) {
    }

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

    ngOnDestroy() {
        this.subscription.unsubscribe();
    }
}
Eray T
sumber
1

Komponen: Berlangganan ke semua acara perutean alih-alih membuat tindakan di templat dan gulir ke NavigationEnd b / c jika tidak Anda akan mengaktifkannya pada navigasi yang buruk atau rute yang diblokir, dll ... Ini adalah cara yang pasti untuk mengetahui bahwa jika rute berhasil dinavigasi, lalu gulir halus. Jika tidak, jangan lakukan apa pun.

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

  router$: Subscription;

  constructor(private router: Router) {}

  ngOnInit() {
    this.router$ = this.router.events.subscribe(next => this.onRouteUpdated(next));
  }

  ngOnDestroy() {
    if (this.router$ != null) {
      this.router$.unsubscribe();
    }
  }

  private onRouteUpdated(event: any): void {
    if (event instanceof NavigationEnd) {
      this.smoothScrollTop();
    }
  }

  private smoothScrollTop(): void {
    const scrollToTop = window.setInterval(() => {
      const pos: number = window.pageYOffset;
      if (pos > 0) {
          window.scrollTo(0, pos - 20); // how far to scroll on each step
      } else {
          window.clearInterval(scrollToTop);
      }
    }, 16);
  }

}

HTML

<router-outlet></router-outlet>
Joshua Michael Wagoner
sumber
1

coba ini

@NgModule({
  imports: [RouterModule.forRoot(routes,{
    scrollPositionRestoration: 'top'
  })],
  exports: [RouterModule]
})

kode ini didukung sudut 6 <=

Rokive
sumber