Kątowa 5 Przewiń do góry po każdym kliknięciu trasy

111

Używam kątowej 5. Mam pulpit nawigacyjny, w którym mam kilka sekcji z małą zawartością i kilka sekcji z tak dużą zawartością, że mam problem ze zmianą routera podczas przechodzenia na górę. Za każdym razem, gdy muszę przewijać, aby przejść do góry. Czy ktoś może mi pomóc rozwiązać ten problem, aby po zmianie routera mój widok był zawsze na górze.

Z góry dziękuję.

raihan
źródło

Odpowiedzi:

233

Jest kilka rozwiązań, koniecznie sprawdź je wszystkie :)


Gniazdo routera wyemituje activatezdarzenie za każdym razem, gdy tworzony jest nowy komponent, więc możemy użyć (activate)do przewijania (na przykład) do góry:

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

Użyj na przykład tego rozwiązania do płynnego przewijania:

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

Jeśli chcesz być selektywny, powiedz, że nie każdy komponent powinien wywoływać przewijanie, możesz to sprawdzić w ifinstrukcji takiej jak ta:

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

Od Angular6.1 możemy również używać { scrollPositionRestoration: 'enabled' }na chętnie ładowanych modułach i będzie to zastosowane do wszystkich tras:

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

Będzie również wykonywać płynne przewijanie. Jednak jest to niewygodne przy każdym routingu.


Innym rozwiązaniem jest przewijanie w górę animacji routera. Dodaj to w każdym przejściu, w którym chcesz przewinąć do góry:

query(':enter, :leave', style({ position: 'fixed' }), { optional: true }) 
Vega
źródło
4
to działa . Dzięki za odpowiedź. oszczędzasz mi dużo czasu
raihan
2
zdarzenia przewijania windowobiektu nie działają w trybie kątowym 5. Jakieś przypuszczenia dlaczego?
Sahil Babbar
2
Prawdziwy bohater. Oszczędność możliwych godzin frustracji. Dziękuję Ci!
Anjana Silva,
1
@AnjanaSilva, dziękuję za pozytywny komentarz! Bardzo się cieszę, że mogła Ci pomóc :)
Vega
2
Czy istnieje sposób na lazyloaded module?
Pankaj Prakash
55

Jeśli napotkasz ten problem w Angular 6, możesz go naprawić, dodając parametr scrollPositionRestoration: 'enabled'do RouterModule app-routing.module.ts:

@NgModule({
  imports: [RouterModule.forRoot(routes,{
    scrollPositionRestoration: 'enabled'
  })],
  exports: [RouterModule]
})
Nimezzz
źródło
7
Prosimy nie wysyłać zdjęć kodu. Po prostu skopiuj i wklej sam kod do swojej odpowiedzi.
mypetlion
2
Pewnie. Następnym razem będę. Jestem tu trochę nowy. :)
Nimezzz
5
Zwróć uwagę, że przynajmniej od 6 listopada 2019 r. Przy użyciu Angular 8 scrollPositionRestorationwłaściwość nie działa z dynamiczną zawartością strony (tj. Gdy
dbeachy1
26

EDYCJA: W przypadku Angular 6+ użyj odpowiedzi Nimesh Nishara Indimagedara, wspominając:

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

Oryginalna odpowiedź:

Jeśli wszystko zawiedzie, utwórz pusty element HTML (np .: div) na górze (lub przewiń do lokalizacji) z id = "top" w szablonie (lub szablonie nadrzędnym):

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

I w komponencie:

  ngAfterViewInit() {
    // Hack: Scrolls to top of Page after page view initialized
    let top = document.getElementById('top');
    if (top !== null) {
      top.scrollIntoView();
      top = null;
    }
  }
GeoRover
źródło
2
To rozwiązanie działało u mnie (testowane zarówno na Chrome, jak i Edge). Przyjęte rozwiązanie nie zadziałało dla mojego projektu (Angular5)
Rob van Meeuwen
@RobvanMeeuwen, Jeśli moja odpowiedź nie zadziałała, prawdopodobnie nie zaimplementowałeś jej w ten sam sposób. To rozwiązanie polega na bezpośredniej manipulacji DOM, która nie jest poprawna ani bezpieczna
Vega
@Vega, dlatego nazwałem to hackem. Twoje rozwiązanie jest właściwe. Niektórzy ludzie tutaj nie byli w stanie zaimplementować twojego, więc zaproponowałem ci awaryjny hack. Powinni refaktoryzować swój kod na podstawie wersji, w której są w tym momencie.
GeoRover
Z całego tego rozwiązania jest dla mnie praca. Dzięki @ GeoRover
Gangani Roshan
W przypadku Angular 6+ użyj odpowiedzi Nimesh Nishara Indimagedara.
GeoRover
6

Chociaż @Vega zapewnia bezpośrednią odpowiedź na twoje pytanie, są pewne problemy. Łamie przycisk wstecz / dalej przeglądarki. Jeśli użytkownik kliknie przycisk wstecz lub do przodu w przeglądarce, straci on swoje miejsce i zostanie przewinięty na górze. Może to być trochę uciążliwe dla użytkowników, jeśli musieli przewinąć w dół, aby dostać się do łącza i zdecydowali się kliknąć wstecz, tylko po to, aby stwierdzić, że pasek przewijania został zresetowany do góry.

Oto moje rozwiązanie problemu.

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
źródło
2
Dziękuję za zaawansowany kod i fajne rozwiązanie. Ale czasami rozwiązanie @Vega jest lepsze, ponieważ rozwiązuje wiele problemów z animacją i dynamiczną wysokością strony. Twoje rozwiązanie jest dobre, jeśli masz długą stronę z treścią i prostą animacją routingu. Wypróbowuję to na stronie z wieloma blokami animacji i dynamiki i nie wygląda to najlepiej. Myślę, że czasami możemy poświęcić „tylną pozycję” dla naszej aplikacji. Ale jeśli nie - Twoje rozwiązanie jest najlepsze, co widzę dla Angulara. Jeszcze raz dziękuję
Denis Savenko
5

W moim przypadku właśnie dodałem

window.scroll(0,0);

w ngOnInit()i jego pracy grzywny.

Zohab Ali
źródło
4

Od wersji Angular 6+ Nie ma potrzeby używania window.scroll (0,0)

Dla wersji Angular 6+od @ Przedstawia opcje konfiguracji routera.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'
}

Można użyć scrollPositionRestoration?: 'disabled' | 'enabled' | 'top' w

Przykład:

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

A jeśli ktoś wymaga ręcznego sterowania przewijaniem, nie ma potrzeby używania window.scroll(0,0) Zamiast tego wprowadzono wspólny pakiet 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
}

Użycie jest dość proste Przykład:

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
źródło
4

jeśli używasz mat-sidenav, podaj identyfikator do gniazda routera (jeśli masz nadrzędne i podrzędne gniazda routera) i użyj w nim funkcji aktywuj <router-outlet id="main-content" (activate)="onActivate($event)"> i użyj tego selektora zapytania „mat-sidenav-content”, aby przewinąć górę onActivate(event) { document.querySelector("mat-sidenav-content").scrollTo(0, 0); }

ivin antony
źródło
Działa świetnie, nawet bez używania id (mam jeden router-outletw mojej aplikacji). Zrobiłem to również w sposób bardziej „kanciasty”: @ViewChild(MatSidenavContainer) sidenavContainer: MatSidenavContainer; onActivate() { this.sidenavContainer.scrollable.scrollTo({ left: 0, top: 0 }); }
GCSDC
3

Ciągle szukam wbudowanego rozwiązania tego problemu, takiego jak w AngularJS. Ale do tego czasu to rozwiązanie działa dla mnie, jest proste i zachowuje funkcjonalność przycisku Wstecz.

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

Odpowiedź użytkownika zurfyx original post

Jared Whipple
źródło
3

Wystarczy stworzyć funkcję, która zawiera regulację przewijania ekranu

na przykład

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

window.scrollTo (xpos, ypos) -> oczekiwany parametr.

Vijay Barot
źródło
3

Angular 6.1 i nowsze:

Możesz skorzystać z wbudowanego rozwiązania dostępnego w Angular 6.1+ z opcją, scrollPositionRestoration: 'enabled'aby osiągnąć to samo.

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

Angular 6.0 i starsze:

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

Uwaga: oczekiwanym zachowaniem jest to, że po powrocie do strony powinna pozostać przewinięta w dół do tej samej lokalizacji, w której znajdowała się po kliknięciu łącza, ale przewijana do góry po przejściu na każdą stronę.

s sharif
źródło
2

Oto rozwiązanie, które przewija się do góry komponentu tylko przy pierwszej wizycie dla KAŻDEGO komponentu (na wypadek, gdybyś musiał zrobić coś innego dla każdego komponentu):

W każdym komponencie:

export class MyComponent implements OnInit {

firstLoad: boolean = true;

...

ngOnInit() {

  if(this.firstLoad) {
    window.scroll(0,0);
    this.firstLoad = false;
  }
  ...
}
0_tr0jan_0
źródło
2

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

}

irhetoryczne
źródło
2

poprostu dodaj

window.scrollTo({ top: 0);

to ngOnInit ()

Max Hesari
źródło
window.scroll (0,0)
Akitha_MJ
2

Dla kogoś, kto szuka funkcji przewijania, po prostu dodaj tę funkcję i wywołaj, gdy zajdzie taka potrzeba

scrollbarTop(){

  window.scroll(0,0);
}
Akitha_MJ
źródło
1

Spróbuj tego:

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
źródło
1

Komponent: zasubskrybuj wszystkie zdarzenia routingu zamiast tworzyć akcję w szablonie i przewijaj na NavigationEnd b / c, w przeciwnym razie uruchomisz to w przypadku złej nawigacji lub zablokowanych tras itp ... Jest to pewny sposób, aby wiedzieć, że jeśli trasa została pomyślnie pokonana, a następnie przewiń. W przeciwnym razie nic nie rób.

@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
źródło
1

Spróbuj tego

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

ten kod obsługuje kątowe 6 <=

Rokive
źródło