Angular 5 เลื่อนขึ้นไปด้านบนทุกครั้งที่คลิกเส้นทาง


117

ฉันใช้ angular 5 ฉันมีแดชบอร์ดที่ฉันมีไม่กี่ส่วนที่มีเนื้อหาขนาดเล็กและไม่กี่ส่วนที่มีเนื้อหาขนาดใหญ่จนฉันประสบปัญหาเมื่อเปลี่ยนเราเตอร์ในขณะที่ขึ้นไปด้านบน ทุกครั้งฉันต้องเลื่อนเพื่อไปด้านบน ใครก็ได้ช่วยฉันแก้ปัญหานี้เพื่อที่ว่าเมื่อฉันเปลี่ยนเราเตอร์มุมมองของฉันจะอยู่ที่ด้านบนเสมอ

ขอบคุณล่วงหน้า.


คำตอบ:


240

มีวิธีแก้ปัญหาบางอย่างอย่าลืมตรวจสอบทั้งหมด :)


เต้ารับเราเตอร์จะปล่อย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 เดาว่าทำไม?
Sahil Babbar

2
ฮีโร่ตัวจริงที่นี่ บันทึกชั่วโมงแห่งความไม่พอใจที่เป็นไปได้ ขอขอบคุณ!
Anjana Silva

1
@AnjanaSilva ขอบคุณสำหรับความคิดเห็นในเชิงบวก! ฉันดีใจมากที่สามารถช่วยคุณได้ :)
Vega

2
มีวิธีใดบ้างสำหรับ lazyloaded module?
Pankaj Prakash

57

หากคุณประสบปัญหานี้ใน Angular 6 คุณสามารถแก้ไขได้โดยเพิ่มพารามิเตอร์scrollPositionRestoration: 'enabled'ไปยัง RouterModule ของ app-Routing.module.ts:

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

5
โปรดทราบว่าในวันที่ 6 พฤศจิกายน 2019 โดยใช้ Angular 8 เป็นอย่างน้อยscrollPositionRestorationคุณสมบัติจะไม่ทำงานกับเนื้อหาของเพจแบบไดนามิก (กล่าวคือเมื่อโหลดเนื้อหาของเพจแบบอะซิงโครนัส): ดูรายงานข้อบกพร่องเชิงมุมนี้: github.com/angular/angular / issue / 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;
    }
  }

2
วิธีนี้ใช้ได้ผลสำหรับฉัน (ทดสอบบน Chrome และ Edge) โซลูชันที่ยอมรับใช้ไม่ได้กับโครงการของฉัน (Angular5)
Rob van Meeuwen

@RobvanMeeuwen ถ้าคำตอบของฉันไม่ได้ผลอาจเป็นเพราะคุณไม่ได้นำไปใช้ในลักษณะเดียวกัน โซลูชันนี้กำลังจัดการ DOM โดยตรงซึ่งไม่ถูกต้องทั้งปลอดภัย
Vega

@ เวก้านั่นคือสาเหตุที่เรียกว่าแฮ็ค ทางออกของคุณเป็นวิธีที่เหมาะสม บางคนที่นี่ไม่สามารถใช้งานของคุณได้ดังนั้นฉันจึงเสนอแฮ็กสำรอง พวกเขาควร refactor รหัสของพวกเขาตามเวอร์ชันที่พวกเขาใช้ ณ จุดนี้
GeoRover

จากวิธีแก้ปัญหาทั้งหมดนี้ใช้ได้ผลสำหรับฉัน ขอบคุณ @GeoRover
Gangani Roshan

สำหรับ Angular 6+ โปรดใช้คำตอบของ Nimesh Nishara Indimagedara
GeoRover


7

แม้ว่า @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 ขอบคุณอีกครั้ง
Denis Savenko

6

จาก 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) แทนจากแพคเกจร่วมกันเชิงมุม 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

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

หมายเหตุ: ลักษณะการทำงานที่คาดไว้คือเมื่อคุณกลับไปที่หน้านั้นควรเลื่อนลงไปยังตำแหน่งเดิมเมื่อคุณคลิกลิงก์ แต่จะเลื่อนไปด้านบนเมื่อมาถึงทุกหน้า


4

หากคุณใช้ mat-sidenav ให้ id กับเต้ารับเราเตอร์ (หากคุณมีเต้ารับเราเตอร์แม่และลูก) และใช้ฟังก์ชันเปิดใช้งานในนั้น <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

สำหรับบางคนที่กำลังมองหาฟังก์ชั่นการเลื่อนเพียงแค่เพิ่มฟังก์ชั่นและโทรเมื่อจำเป็น

scrollbarTop(){

  window.scroll(0,0);
}

2

นี่คือวิธีแก้ปัญหาที่เลื่อนขึ้นไปด้านบนของ Component เท่านั้นหากครั้งแรกที่เข้าไปที่องค์ประกอบ EACH (ในกรณีที่คุณต้องทำบางอย่างที่แตกต่างไปตามแต่ละองค์ประกอบ):

ในแต่ละส่วนประกอบ:

export class MyComponent implements OnInit {

firstLoad: boolean = true;

...

ngOnInit() {

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


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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.