Angolare 5 Scorri verso l'alto ad ogni clic sul percorso


99

Sto usando Angular 5. Ho una dashboard in cui ho poche sezioni con contenuti piccoli e poche sezioni con contenuti così grandi che sto affrontando un problema quando cambio router mentre vado all'inizio. Ogni volta che devo scorrere per andare in alto. Qualcuno può aiutarmi a risolvere questo problema in modo che quando cambio il router la mia vista rimanga sempre in alto.

Grazie in anticipo.


Risposte:


213

Ci sono alcune soluzioni, assicurati di controllarle tutte :)


La presa del router emetterà l'evento "activ" ogni volta che viene istanziato un nuovo componente, quindi potremmo usare "(attivare)" per scorrere (ad esempio) verso l'alto:

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

Usa questa risposta per uno scorrimento fluido:

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

Se desideri essere selettivo, diciamo che non tutti i componenti dovrebbero attivare lo scorrimento, puoi controllarlo, quindi:

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

A partire da Angular6.1, possiamo anche usare "{scrollPositionRestoration: 'enabled'}" sui moduli caricati con entusiasmo e verrà applicato a tutte le rotte:
RouterModule.forRoot(appRoutes, { scrollPositionRestoration: 'enabled' })

Eseguirà anche lo scorrimento fluido. Tuttavia questo ha l'inconveniente di farlo su ogni percorso.


Un'altra soluzione è eseguire lo scorrimento dall'alto sull'animazione del router. Aggiungilo in ogni transizione in cui desideri scorrere verso l'alto:
query(':enter, :leave', style({ position: 'fixed' }), { optional: true }) 

4
sta funzionando . Grazie per la tua risposta. risparmi un sacco di tempo per me
raihan

2
gli eventi di scorrimento windowsull'oggetto non funzionano in angolare 5. Qualche ipotesi perché?
Sahil Babbar

2
Un vero eroe qui. Risparmiato ore possibili di frustrazione. Grazie!
Anjana Silva

1
@AnjanaSilva, grazie per il commento positivo! Sono molto contento che possa aiutarti :)
Vega

2
C'è un modo per il modulo lazyloaded?
Pankaj Prakash

54

Se affronti questo problema in Angular 6, puoi risolverlo aggiungendo il parametro scrollPositionRestoration: 'enabled'al RouterModule di app-routing.module.ts:

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

7
Si prega di non pubblicare immagini di codice. Basta copiare e incollare il codice stesso direttamente nella risposta.
mypetlion

2
Sicuro. La prossima volta lo farò. Sono solo un po 'nuovo qui. :)
Nimezzz

4
Si noti che a partire dal 6 novembre 2019 utilizzando Angular 8, almeno, la scrollPositionRestorationproprietà non funziona con il contenuto della pagina dinamica (ovvero, dove il contenuto della pagina viene caricato in modo asincrono): vedere questo rapporto sui bug di Angular: github.com/angular/angular / issues / 24547
dbeachy1

25

EDIT: per Angular 6+, usa la risposta di Nimesh Nishara Indimagedara menzionando:

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

Risposta originale:

Se tutto fallisce, crea un elemento HTML vuoto (ad esempio: div) in alto (o scorri fino alla posizione desiderata) con id = "top" sul modello (o modello principale):

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

E in componente:

  ngAfterViewInit() {
    // Hack: Scrolls to top of Page after page view initialized
    let top = document.getElementById('top');
    if (top !== null) {
      top.scrollIntoView();
      top = null;
    }
  }

2
Questa soluzione ha funzionato per me (testata su Chrome e Edge). La soluzione accettata non ha funzionato per il mio progetto (Angular5)
Rob van Meeuwen

@ RobvanMeeuwen, se la mia risposta non ha funzionato, probabilmente non l'hai implementata allo stesso modo. Questa soluzione sta manipolando direttamente il DOM che non è corretto né sicuro
Vega

@Vega, ecco perché l'ho chiamato hack. La tua soluzione è quella giusta. Alcune persone qui non sono state in grado di implementare il tuo, quindi ho offerto l'hack di riserva. Dovrebbero eseguire il refactoring del codice in base alla versione in cui si trovano a questo punto.
GeoRover

Da tutta questa soluzione è il lavoro per me. Grazie @GeoRover
Gangani Roshan

Per Angular 6+, usa la risposta di Nimesh Nishara Indimagedara.
GeoRover


6

Sebbene @Vega fornisca la risposta diretta alla tua domanda, ci sono problemi. Interrompe il pulsante Indietro / Avanti del browser. Se l'utente fa clic sul pulsante Indietro o Avanti del browser, perde il suo posto e viene fatto scorrere in alto. Questo può essere un po 'fastidioso per i tuoi utenti se dovessero scorrere verso il basso per arrivare a un link e decidessero di fare clic indietro solo per scoprire che la barra di scorrimento era stata reimpostata in alto.

Ecco la mia soluzione al problema.

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

2
Grazie per il codice avanzato e la bella soluzione. Ma a volte la soluzione @Vega è migliore, perché risolve molti problemi con l'animazione e l'altezza della pagina dinamica. La tua soluzione è buona se hai una pagina lunga con contenuto e semplice animazione di instradamento. Lo provo sulla pagina con molte animazioni e blocchi dinamici e non sembra così buono. Penso che a volte possiamo sacrificare la "posizione posteriore" per la nostra app. Ma in caso contrario, la tua soluzione è la migliore che vedo per Angular. Grazie ancora
Denis Savenko

5

Nel mio caso ho appena aggiunto

window.scroll(0,0);

in ngOnInit()e funziona bene.


4

Dalla versione angolare 6+ Non è necessario utilizzare window.scroll (0,0)

Per la versione angolare 6+da @ Rappresenta le opzioni per configurare il 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'
}

Si può usare scrollPositionRestoration?: 'disabled' | 'enabled' | 'top'in

Esempio:

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

E se è necessario controllare manualmente lo scorrimento, non è necessario utilizzarlo window.scroll(0,0) pacchetto comune invece di Angular V6 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
}

L'utilizzo è piuttosto semplice Esempio:

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

4

se stai usando mat-sidenav dai un ID alla presa del router (se hai una presa del router genitore e figlio) e usa la funzione di attivazione in essa <router-outlet id="main-content" (activate)="onActivate($event)"> e usa questo selettore di query 'mat-sidenav-content' per scorrere in alto onActivate(event) { document.querySelector("mat-sidenav-content").scrollTo(0, 0); }


Funziona alla grande anche senza usare un id (ne ho uno router-outletsulla mia app). L'ho fatto anche in modo più "angolare": @ViewChild(MatSidenavContainer) sidenavContainer: MatSidenavContainer; onActivate() { this.sidenavContainer.scrollable.scrollTo({ left: 0, top: 0 }); }
GCSDC

3

Continuo a cercare una soluzione integrata a questo problema come c'è in AngularJS. Ma fino ad allora questa soluzione funziona per me, è semplice e preserva la funzionalità del pulsante Indietro.

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

Risposta dal post originale di zurfyx


3

Hai solo bisogno di creare una funzione che contenga la regolazione dello scorrimento dello schermo

per esempio

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

window.scrollTo (xpos, ypos) -> parametro atteso.


2

Ecco una soluzione che scorre fino all'inizio del componente solo se la prima volta si visita per OGNI componente (nel caso in cui sia necessario fare qualcosa di diverso per componente):

In ogni componente:

export class MyComponent implements OnInit {

firstLoad: boolean = true;

...

ngOnInit() {

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

2

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

}


2

Angular 6.1 e versioni successive:

È possibile utilizzare la soluzione integrata disponibile in Angular 6.1+ con l'opzione scrollPositionRestoration: 'enabled'per ottenere lo stesso risultato.

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

Angular 6.0 e versioni precedenti:

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

Nota: il comportamento previsto è che quando torni alla pagina, questa dovrebbe rimanere scorrere verso il basso fino alla stessa posizione in cui era quando hai fatto clic sul collegamento, ma scorrere verso l'alto quando si arriva a ogni pagina.



2

Per qualcuno che sta cercando la funzione di scorrimento basta aggiungere la funzione e chiamare quando necessario

scrollbarTop(){

  window.scroll(0,0);
}

1

Prova questo:

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

1

Componente: iscriviti a tutti gli eventi di instradamento invece di creare un'azione nel modello e scorri su NavigationEnd b / c altrimenti lo spegnerai su nav cattive o rotte bloccate, ecc ... Questo è un modo sicuro per sapere che se una rotta è stata raggiunta con successo, quindi scorrere. Altrimenti, non fare nulla.

@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>

1

prova questo

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

questo codice supportava angolare 6 <=

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.