Angular 5 Прокрутка вверх при каждом нажатии Route

113

Я использую angular 5. У меня есть панель инструментов, где у меня есть несколько разделов с небольшим содержанием и несколько разделов с таким большим содержанием, что я сталкиваюсь с проблемой при смене маршрутизатора при переходе наверх. Каждый раз мне нужно прокрутить, чтобы перейти наверх. Может ли кто-нибудь помочь мне решить эту проблему, чтобы при смене маршрутизатора мое мнение всегда оставалось наверху.

Заранее спасибо.

Райхан
источник
Возможный дубликат Angular 2 Scroll to top при изменении маршрута
Cerbrus

Ответы:

234

Есть несколько решений, обязательно проверьте их все :)


Выход маршрутизатора будет генерировать 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
это работает . Спасибо за Ваш ответ. ты
экономишь
2
события прокрутки windowобъекта не работают в angular 5. Есть предположения, почему?
Сахил Баббар
2
Здесь настоящий герой. Сэкономил возможные часы разочарования. Спасибо!
Анджана Сильва
1
@AnjanaSilva, спасибо за положительный комментарий! Я очень рада, что это могло вам помочь :)
Vega
2
Есть ли способ для ленивого модуля?
Панкадж Пракаш
56

Если вы столкнулись с этой проблемой в Angular 6, вы можете исправить ее, добавив параметр scrollPositionRestoration: 'enabled'в RouterModule app-routing.module.ts:

@NgModule({
  imports: [RouterModule.forRoot(routes,{
    scrollPositionRestoration: 'enabled'
  })],
  exports: [RouterModule]
})
Nimezzz
источник
5
Обратите внимание, что с 6 ноября 2019 года при использовании Angular 8, по крайней мере, scrollPositionRestorationсвойство не работает с динамическим содержимым страницы (то есть, когда содержимое страницы загружается асинхронно): см. Этот отчет об ошибке Angular: github.com/angular/angular / issues / 24547
dbeachy1
27

РЕДАКТИРОВАТЬ: для Angular 6+ используйте ответ Nimesh Nishara Indimagedara, в котором упоминается:

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;
    }
  }
GeoRover
источник
2
Это решение сработало для меня (проверено как в Chrome, так и в Edge). Принятое решение не сработало для моего проекта (Angular5)
Роб ван Миувен
@RobvanMeeuwen, если мой ответ не сработал, вероятно, вы не реализовали его таким же образом. Это решение напрямую манипулирует DOM, что неверно и безопасно
Vega
@Vega, вот почему я назвал это хаком. Ваше решение правильное. Некоторым здесь не удалось реализовать вашу, поэтому я предложил запасной вариант. Им следует провести рефакторинг своего кода в зависимости от версии, в которой они сейчас используются.
GeoRover
Из всего этого решение для меня работает. Спасибо @GeoRover
Гангани Рошан
Для Angular 6+ используйте ответ Nimesh Nishara Indimagedara.
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;
      }
    });
  }
}
Sal_Vader_808
источник
2
Спасибо за продвинутый код и хорошее решение. Но иногда решение @Vega лучше, потому что оно решает многие проблемы с анимацией и динамической высотой страницы. Ваше решение хорошо, если у вас длинная страница с контентом и простой анимацией маршрутизации. Пробую на странице с большим количеством блоков анимации и динамики, выглядит не очень хорошо. Я думаю, что иногда мы можем пожертвовать «позицией на спине» ради нашего приложения. Но если нет - ваше решение - лучшее, что я вижу для Angular. Еще раз спасибо
Денис Савенко
5

В моем случае я просто добавил

window.scroll(0,0);

in 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) -> ожидаемый параметр.

Виджей Барот
источник
3

Angular 6.1 и новее:

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

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

Angular 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

Вот решение, которое прокручивается в начало компонента только при первом посещении КАЖДОГО компонента (в случае, если вам нужно сделать что-то другое для каждого компонента):

В каждом компоненте:

export class MyComponent implements OnInit {

firstLoad: boolean = true;

...

ngOnInit() {

  if(this.firstLoad) {
    window.scroll(0,0);
    this.firstLoad = false;
  }
  ...
}
0_tr0jan_0
источник
2

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

}

ирторика
источник
2

просто добавь

window.scrollTo({ top: 0);

в ngOnInit ()

Макс Хесари
источник
window.scroll (0,0)
Akitha_MJ
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();
    }
}
Eray T
источник
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 <=

Рокиве
источник