Кутовий 5 Прокрутіть угору при кожному клацанні маршруту


103

Я використовую angular 5. У мене є інформаційна панель, де у мене є кілька розділів з невеликим вмістом і кілька розділів з таким великим вмістом, що я стикаюся з проблемою при зміні маршрутизатора під час переходу вгору. Кожного разу, коли мені потрібно прокрутити, щоб перейти до початку. Хто-небудь може допомогти мені вирішити цю проблему, щоб при зміні маршрутизатора мій погляд завжди залишався на вершині.

Заздалегідь спасибі.


Відповіді:


218

Є кілька рішень, обов’язково перевірте їх усі :)


Розетка маршрутизатора видаватиме activateподію щоразу, коли створюється інстанція нового компонента, тому ми могли б скористатися (activate)для прокрутки (наприклад) до початку:

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

Для прикладу використовуйте це рішення для плавного прокручування:

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

Якщо ви хочете бути вибірковим, скажімо, не кожен компонент повинен запускати прокрутку, ви можете перевірити це в такому ifтвердженні:

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

Починаючи з Angular6.1, ми також можемо використовувати { scrollPositionRestoration: 'enabled' }модулі, що завантажуються, і це буде застосовано до всіх маршрутів:

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

Він також вже виконуватиме плавну прокрутку. Однак це незручно робити це на кожному маршруті.


Іншим рішенням є прокрутка зверху на анімації маршрутизатора. Додайте це в кожному переході, де ви хочете прокрутити угору:

query(':enter, :leave', style({ position: 'fixed' }), { optional: true }) 

4
це працює . Дякую за вашу відповідь. Ви економите для мене багато часу
raihan

2
події прокрутки на windowоб'єкті не працюють в кутовому 5. Будь-які здогадки, чому?
Сахіл Баббар,

2
Тут справжній герой. Збережено можливі години розчарування. Дякую!
Anjana Silva

1
@AnjanaSilva, дякую за позитивний коментар! Я дуже радий, що це може вам допомогти :)
Вега,

2
Чи є якийсь спосіб для модуля, який завантажується з ледачим?
Панкадж Пракаш

54

Якщо ви зіткнулися з цією проблемою в Angular 6, ви можете її виправити, додавши параметр scrollPositionRestoration: 'enabled'до RouterModule app-routing.module.ts:

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

7
Будь ласка, не публікуйте фотографії коду. Просто скопіюйте і вставте сам код прямо у свою відповідь.
mypetlion

2
Звичайно. Наступного разу буду. Я просто щось нове тут. :)
Nimezzz

4
Зверніть увагу, що станом на 6 листопада 2019 року, використовуючи Angular 8, scrollPositionRestorationвластивість не працює з динамічним вмістом сторінки (тобто там, де вміст сторінки завантажується асинхронно): див. Цей звіт про помилки Angular: github.com/angular/angular / issues / 24547
dbeachy1

25

РЕДАКТУВАТИ: Для Angular 6+, будь ласка, використовуйте відповідь Німеша Нішари Індімагедари із зазначенням:

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

Оригінальна відповідь:

Якщо все не вдається, створіть якийсь порожній елемент HTML (наприклад: div) вгорі (або бажане прокручування до місця) з id = "top" у шаблоні (або батьківському шаблоні):

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

А в компоненті:

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

2
Це рішення працювало для мене (протестовано на Chrome, а також Edge). Прийняте рішення не спрацювало для мого проекту (Angular5)
Роб ван Меуен,

@RobvanMeeuwen, Якщо моя відповідь не спрацювала, можливо, ви не так реалізували її. Це рішення безпосередньо маніпулює DOM, що не є правильним і безпечним
Вега,

@Vega, ось чому я назвав це хакерством. Ваше рішення є правильним. Деякі люди тут не змогли реалізувати ваші, тому я запропонував резервний хак. Вони повинні переробляти свій код на основі версії, на якій вони працюють на даний момент.
GeoRover

З усього цього рішення - робота для мене. Дякую @GeoRover
Gangani Roshan

Для Angular 6+ використовуйте відповідь Німеша Нішари Індімагедари.
GeoRover


6

Хоча @Vega дає пряму відповідь на ваше запитання, є проблеми. Це ламає кнопку браузера назад / вперед. Якщо ви користувач натискаєте кнопку браузера назад або вперед, вони втрачають своє місце і отримують прокрутку вгорі. Це може трохи нашкодити вашим користувачам, якщо їм потрібно було прокрутити вниз, щоб дістатися до посилання, і вирішили натиснути назад лише для того, щоб знайти смужку прокрутки, яка була скинута до початку.

Ось моє рішення проблеми.

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
Дякуємо за вдосконалений код та гарне рішення. Але іноді рішення @Vega є кращим, оскільки воно вирішує багато проблем з анімацією та динамічною висотою сторінки. Ваше рішення добре, якщо у вас довга сторінка із вмістом і проста анімація маршрутизації. Я пробую це на сторінці з багатьма блоками анімації та динаміки, і це виглядає не дуже добре. Я думаю, що іноді ми можемо пожертвувати `` задньою позицією '' для нашого додатка. Але якщо ні - ваше рішення - найкраще, що я бачу для Angular. Ще раз спасибі
Денис Савенко

5

У моєму випадку я просто додав

window.scroll(0,0);

в ngOnInit()і працює нормально.


4

З Angular версії 6+ Не потрібно використовувати window.scroll (0,0)

Для Angular версії 6+від @ Представляє варіанти налаштування маршрутизатора.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'
}

Можна використовувати scrollPositionRestoration?: 'disabled' | 'enabled' | 'top'в

Приклад:

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

І якщо вам потрібно вручну керувати прокруткою, не потрібно використовувати window.scroll(0,0) замість цього з 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
}

Використання є досить простим Приклад:

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

якщо ви використовуєте mat-sidenav, дайте ідентифікатор розетці маршрутизатора (якщо у вас є батьківський і дочірній розетки маршрутизатора) і використовуйте в ньому функцію активації <router-outlet id="main-content" (activate)="onActivate($event)"> та використовуйте цей селектор запитів 'mat-sidenav-content' для прокрутки зверху onActivate(event) { document.querySelector("mat-sidenav-content").scrollTo(0, 0); }


Чудово працює навіть без використання id (у мене в router-outletдодатку є сингл ). Я також зробив це більш «кутовим способом»: @ViewChild(MatSidenavContainer) sidenavContainer: MatSidenavContainer; onActivate() { this.sidenavContainer.scrollable.scrollTo({ left: 0, top: 0 }); }
GCSDC

3

Я продовжую шукати вбудоване рішення цієї проблеми, як у AngularJS. Але до тих пір це рішення працює для мене, воно просте і зберігає функціональність кнопки повернення.

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

Відповідь з оригінального допису zurfyx


3

Вам просто потрібно створити функцію, яка містить налаштування прокрутки екрана

наприклад

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

window.scrollTo (xpos, ypos) -> очікуваний параметр.


2

Ось рішення, яке прокручується до вершини Компонента лише при першому відвідуванні КОЖНОГО компонента (якщо вам потрібно зробити щось інше для кожного компонента):

У кожному компоненті:

export class MyComponent implements OnInit {

firstLoad: boolean = true;

...

ngOnInit() {

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


2

Кутова 6.1 та новіша версія:

Ви можете використовувати вбудоване рішення, доступне в Angular 6.1+, з можливістю scrollPositionRestoration: 'enabled'досягти того самого.

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

Кутовий 6.0 і раніше:

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

Примітка: Очікувана поведінка полягає в тому, що коли ви повертаєтесь до сторінки, вона повинна залишатися прокрученою вниз до того самого місця, де вона була, коли ви натискали на посилання, але прокручуючи угорі, коли переходите на кожну сторінку.



2

Для когось, хто шукає функцію прокрутки, просто додайте функцію та зателефонуйте, коли це буде потрібно

scrollbarTop(){

  window.scroll(0,0);
}

1

Спробуйте це:

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

Компонент: Підпишіться на всі події маршрутизації, замість того, щоб створювати дію в шаблоні та прокручуйте NavigationEnd b / c, інакше ви вимкнете це на поганих навігаційних або заблокованих маршрутах тощо ... Це надійний пожежний спосіб дізнатися, що якщо маршрут успішно орієнтований, потім спокійне прокручування. Інакше нічого не робити.

@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

спробуйте це

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

цей код підтримував кутовий 6 <=

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.