Angular 2 Desplácese hacia arriba en Cambio de ruta

296

En mi aplicación Angular 2 cuando me desplazo hacia abajo en una página y hago clic en el enlace en la parte inferior de la página, cambia la ruta y me lleva a la página siguiente, pero no se desplaza hacia la parte superior de la página. Como resultado, si la primera página es larga y la segunda página tiene pocos contenidos, da la impresión de que la segunda página carece de contenido. Dado que los contenidos son visibles solo si el usuario se desplaza hacia la parte superior de la página.

Puedo desplazar la ventana a la parte superior de la página en ngInit del componente pero, ¿hay alguna solución mejor que pueda manejar automáticamente todas las rutas en mi aplicación?

Naveed Ahmed
fuente
20
Desde Angular 6.1 podemos usar {scrollPositionRestoration: 'enabled'} en módulos cargados con entusiasmo o solo en app.module y se aplicará a todas las rutas. RouterModule.forRoot(appRoutes, { scrollPositionRestoration: 'enabled' })
Manwal
Muito obrigado sua solução funcionou perfeitamente for mim :)
Raphael Godoi
¿Ninguna persona mencionó el enfoque? es más importante que nunca admitir adecuadamente la accesibilidad / lectores de pantalla y si simplemente se desplaza hacia la parte superior sin considerar el enfoque, la siguiente pulsación de tecla de pestaña puede saltar a la parte inferior de la pantalla.
Simon_Weaver

Respuestas:

386

Puede registrar una escucha de cambio de ruta en su componente principal y desplazarse hacia arriba en los cambios de ruta.

import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';

@Component({
    selector: 'my-app',
    template: '<ng-content></ng-content>',
})
export class MyAppComponent implements OnInit {
    constructor(private router: Router) { }

    ngOnInit() {
        this.router.events.subscribe((evt) => {
            if (!(evt instanceof NavigationEnd)) {
                return;
            }
            window.scrollTo(0, 0)
        });
    }
}
Guilherme Meireles
fuente
11
window.scrollTo(0, 0)es una document.body.scrollTop = 0;OMI más concisa y más legible.
Mark E. Haase
10
¿Alguien notó que incluso después de implementar esto, el problema persiste en el navegador safari de Iphone? ¿Alguna idea?
rgk
1
@mehaase Parece que tu respuesta es la mejor. window.body.scrollTop no funciona para mí en el escritorio de Firefox. Así que gracias !
KCarnaille
3
Esto funcionó para mí, pero rompe el comportamiento predeterminado del botón "atrás". Al retroceder debe recordar la posición de desplazamiento anterior.
JackKalish
66
Esto funcionó !! Aunque agregué en $("body").animate({ scrollTop: 0 }, 1000);lugar de window.scrollTo(0, 0)animar el desplazamiento suave hacia arriba
Manubhargav
360

Angular 6.1 y posterior :

Angular 6.1 (lanzado el 25-07-2018) agregó soporte incorporado para manejar este problema, a través de una función llamada "Restauración de la posición de desplazamiento del enrutador". Como se describe en el blog oficial de Angular , solo necesita habilitar esto en la configuración del enrutador de esta manera:

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

Además, el blog dice "Se espera que esto se convierta en el predeterminado en una versión principal futura". Hasta ahora, esto no ha sucedido (a partir de Angular 8.2), pero eventualmente no necesitará hacer nada en su código, y esto simplemente funcionará correctamente de fábrica.

Puede ver más detalles sobre esta característica y cómo personalizar este comportamiento en los documentos oficiales .

Angular 6.0 y anterior :

Si bien la excelente respuesta de @ GuilhermeMeireles soluciona el problema original, presenta una nueva, al romper el comportamiento normal que espera cuando navega hacia atrás o hacia adelante (con los botones del navegador o mediante la Ubicación en el código). El comportamiento esperado es que cuando navega de regreso a la página, debe permanecer desplazado hacia abajo a la misma ubicación que cuando hizo clic en el enlace, pero desplazarse a la parte superior al llegar a cada página obviamente rompe esta expectativa.

El siguiente código expande la lógica para detectar este tipo de navegación al suscribirse a la secuencia PopStateEvent de Location y omitir la lógica de desplazamiento hacia arriba si la página recién llegada es el resultado de tal evento.

Si la página desde la que navega es lo suficientemente larga como para cubrir toda la ventana gráfica, la posición de desplazamiento se restaura automáticamente, pero como @JordanNelson señaló correctamente, si la página es más corta, debe realizar un seguimiento de la posición de desplazamiento original y restaurarla. explícitamente cuando vuelves a la página. La versión actualizada del código cubre este caso también, siempre restaurando explícitamente la posición de desplazamiento.

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);
            }
        });
    }
}
Fernando Echeverria
fuente
2
Esto debería ir directamente en el componente de la aplicación o en un solo componente utilizado en él (y, por lo tanto, compartido por toda la aplicación). Por ejemplo, lo he incluido en un componente superior de la barra de navegación. No debe incluirse en todos sus componentes.
Fernando Echeverria
3
Puede hacerlo y hará que el código sea más ampliamente compatible con otras plataformas que no sean del navegador. Consulte stackoverflow.com/q/34177221/2858481 para obtener detalles sobre la implementación.
Fernando Echeverria
3
Si hace clic y mantiene presionado el botón de retroceso / avance en los navegadores modernos, aparece un menú que le permite navegar a ubicaciones que no sean la anterior o la siguiente. Esta solución se rompe si haces eso. Es un caso marginal para la mayoría, pero vale la pena mencionarlo.
adamdport
1
¿Hay alguna manera de habilitar la "Restauración de la posición de desplazamiento del enrutador" para elementos anidados o solo funciona para body?
vulp
61

Desde Angular 6.1, ahora puede evitar la molestia y pasar extraOptionsa su RouterModule.forRoot()como segundo parámetro y puede especificar scrollPositionRestoration: enableddecirle a Angular que se desplace hacia arriba cada vez que cambie la ruta.

Por defecto lo encontrarás en app-routing.module.ts:

const routes: Routes = [
  {
    path: '...'
    component: ...
  },
  ...
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {
      scrollPositionRestoration: 'enabled', // Add options right here
    })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Documentos oficiales angulares

Abdul Rafay
fuente
3
A pesar de que la respuesta anterior es más descriptiva, me gusta que esta respuesta me dijera exactamente dónde debe ir esto
ryanovas
32

Puede escribir esto de manera más sucinta aprovechando el filtermétodo observable :

this.router.events.filter(event => event instanceof NavigationEnd).subscribe(() => {
      this.window.scrollTo(0, 0);
});

Si tiene problemas para desplazarse hacia la parte superior al usar el material angular de Sidenav 2, esto será útil. La ventana o el cuerpo del documento no tendrán la barra de desplazamiento, por lo que debe obtener el sidenavcontenedor de contenido y desplazar ese elemento; de lo contrario, intente desplazar la ventana por defecto.

this.router.events.filter(event => event instanceof NavigationEnd)
  .subscribe(() => {
      const contentContainer = document.querySelector('.mat-sidenav-content') || this.window;
      contentContainer.scrollTo(0, 0);
});

Además, Angular CDK v6.x tiene un paquete de desplazamiento ahora que podría ayudar con el manejo del desplazamiento.

mtpultz
fuente
2
¡Excelente! Para mí eso funcionó -document.querySelector('.mat-sidenav-content .content-div').scrollTop = 0;
Amir Tugi
Buenos amigos ... en mtpultz y @AmirTugi. Enfrentando esto ahora mismo, ¡y me lo diste, saludos! Probablemente terminará rodando mi propio navegador lateral ya que el Material 2 no funciona bien cuando md-toolbar está en posición: fijo (en la parte superior). A menos que ustedes tengan ideas ... ????
Tim Harker
Podría haber encontrado mi respuesta ... stackoverflow.com/a/40396105/3389046
Tim Harker
16

Si tiene renderizado del lado del servidor, debe tener cuidado de no ejecutar el código utilizando windowsen el servidor, donde esa variable no existe. Resultaría en la ruptura del código.

export class AppComponent implements OnInit {
  routerSubscription: Subscription;

  constructor(private router: Router,
              @Inject(PLATFORM_ID) private platformId: any) {}

  ngOnInit() {
    if (isPlatformBrowser(this.platformId)) {
      this.routerSubscription = this.router.events
        .filter(event => event instanceof NavigationEnd)
        .subscribe(event => {
          window.scrollTo(0, 0);
        });
    }
  }

  ngOnDestroy() {
    this.routerSubscription.unsubscribe();
  }
}

isPlatformBrowseres una función que se usa para verificar si la plataforma actual donde se representa la aplicación es un navegador o no. Le damos lo inyectado platformId.

También es posible verificar la existencia de variables windows, para estar seguro, así:

if (typeof window != 'undefined')
Rapaz
fuente
1
¿No necesita inyectar PLATFORM_IDen constructory dar este valor como parámetro en el isPlatformBrowsermétodo?
Poul Kruijt
1
@PierreDuc Sí, la respuesta es incorrecta. isPlatformBrowseres una función y siempre será veraz. Lo he editado ahora.
Lazar Ljubenović
¡Gracias! ¡Es correcto ahora! Acabo de verificar la API: github.com/angular/angular/blob/…
Raptor
13

solo hazlo fácil con la acción de hacer clic

en su componente principal html haga referencia #scrollContainer

<div class="main-container" #scrollContainer>
    <router-outlet (activate)="onActivate($event, scrollContainer)"></router-outlet>
</div>

en el componente principal .ts

onActivate(e, scrollContainer) {
    scrollContainer.scrollTop = 0;
}
SE SENTÓ
fuente
El elemento que se desplaza podría no estar en el scrollContainerprimer nodo, puede que tenga que cavar un poco en el objeto, para mí lo que realmente trabajadas fuescrollContainer .scrollable._elementRef.nativeElement.scrollTop = 0
Byron López
13

Angular introdujo recientemente una nueva característica, dentro del módulo de enrutamiento angular realiza cambios como a continuación

@NgModule({
  imports: [RouterModule.forRoot(routes,{
    scrollPositionRestoration: 'top'
  })],
Pran RV
fuente
12

La mejor respuesta reside en la discusión de Angular GitHub ( Cambiar la ruta no se desplaza hacia arriba en la nueva página ).

Tal vez desee ir al principio solo en los cambios del enrutador raíz (no en los hijos, porque puede cargar rutas con carga diferida en un conjunto de pestañas)

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

Créditos completos a JoniJnm ( publicación original )

zurfyx
fuente
7

A partir de Angular 6.1, el enrutador proporciona una opción de configuración llamada scrollPositionRestoration, que está diseñada para satisfacer este escenario.

imports: [
  RouterModule.forRoot(routes, {
    scrollPositionRestoration: 'enabled'
  }),
  ...
]
Marty A
fuente
4

Si necesita simplemente desplazarse por la página hacia arriba, puede hacerlo (no es la mejor solución, pero es rápido)

document.getElementById('elementId').scrollTop = 0;
Aliaksei
fuente
4

Aquí hay una solución que se me ocurrió. Emparejé la estrategia de ubicación con los eventos del enrutador. Usando LocationStrategy para establecer un valor booleano para saber cuándo un usuario atraviesa actualmente el historial del navegador. De esta manera, no tengo que almacenar un montón de URL y datos de desplazamiento en y (que de todos modos no funciona bien, ya que cada información se reemplaza en función de la URL). Esto también resuelve el caso límite cuando un usuario decide mantener presionado el botón de retroceso o avance en un navegador y retrocede o reenvía varias páginas en lugar de solo una.

PD: solo he probado en la última versión de IE, Chrome, Firefox, Safari y Opera (a partir de esta publicación).

Espero que esto ayude.

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
fuente
4

Esta solución se basa en la solución de @ FernandoEcheverria y @ GuilhermeMeireles, pero es más concisa y funciona con los mecanismos de popstate que proporciona Angular Router. Esto permite almacenar y restaurar el nivel de desplazamiento de múltiples navegaciones consecutivas.

Almacenamos las posiciones de desplazamiento para cada estado de navegación en un mapa scrollLevels. Una vez que hay un evento popstate, el ID del estado que está a punto de ser restaurado es suministrada por el router Angular: event.restoredState.navigationId. Esto se usa para obtener el último nivel de desplazamiento de ese estado scrollLevels.

Si no hay un nivel de desplazamiento almacenado para la ruta, se desplazará a la parte superior como era de esperar.

import { Component, OnInit } from '@angular/core';
import { Router, NavigationStart, NavigationEnd } from '@angular/router';

@Component({
    selector: 'my-app',
    template: '<ng-content></ng-content>',
})
export class AppComponent implements OnInit {

  constructor(private router: Router) { }

  ngOnInit() {
    const scrollLevels: { [navigationId: number]: number } = {};
    let lastId = 0;
    let restoredId: number;

    this.router.events.subscribe((event: Event) => {

      if (event instanceof NavigationStart) {
        scrollLevels[lastId] = window.scrollY;
        lastId = event.id;
        restoredId = event.restoredState ? event.restoredState.navigationId : undefined;
      }

      if (event instanceof NavigationEnd) {
        if (restoredId) {
          // Optional: Wrap a timeout around the next line to wait for
          // the component to finish loading
          window.scrollTo(0, scrollLevels[restoredId] || 0);
        } else {
          window.scrollTo(0, 0);
        }
      }

    });
  }

}
Simon Mathewson
fuente
Increíble. Tuve que hacer una versión ligeramente personalizada para desplazar un div en lugar de una ventana, pero funcionó. Una diferencia clave era scrollTopvs scrollY.
BBaysinger
4

Además de la respuesta perfecta proporcionada por @Guilherme Meireles como se muestra a continuación, puede modificar su implementación agregando un desplazamiento suave como se muestra a continuación

 import { Component, OnInit } from '@angular/core';
    import { Router, NavigationEnd } from '@angular/router';

    @Component({
        selector: 'my-app',
        template: '<ng-content></ng-content>',
    })
    export class MyAppComponent implements OnInit {
        constructor(private router: Router) { }

        ngOnInit() {
            this.router.events.subscribe((evt) => {
                if (!(evt instanceof NavigationEnd)) {
                    return;
                }
                window.scrollTo(0, 0)
            });
        }
    }

luego agregue el fragmento a continuación

 html {
      scroll-behavior: smooth;
    }

a tus styles.css

Ifesinachi Bryan
fuente
1

para iphone / ios safari puedes envolver con un setTimeout

setTimeout(function(){
    window.scrollTo(0, 1);
}, 0);
tubbsy
fuente
en mi caso también requería que el elemento de ajuste de página css se estableciera en; height: 100vh + 1px;
tubbsy
1

Hola chicos, esto funciona para mí en angular 4. Solo tienes que hacer referencia al padre para desplazarte en el cambio de enrutador`

layout.component.pug

.wrapper(#outlet="")
    router-outlet((activate)='routerActivate($event,outlet)')

layout.component.ts

 public routerActivate(event,outlet){
    outlet.scrollTop = 0;
 }`
Jonas Arguelles
fuente
1
disculpe mi pereza por no molestarme en aprender pug, pero ¿puede traducir a HTML?
CodyBugstein
0

@Fernando Echeverria genial! pero este código no funciona en enrutadores hash o enrutadores perezosos. porque no desencadenan cambios de ubicación. puede intentar esto:

private lastRouteUrl: string[] = []
  

ngOnInit(): void {
  this.router.events.subscribe((ev) => {
    const len = this.lastRouteUrl.length
    if (ev instanceof NavigationEnd) {
      this.lastRouteUrl.push(ev.url)
      if (len > 1 && ev.url === this.lastRouteUrl[len - 2]) {
        return
      }
      window.scrollTo(0, 0)
    }
  })
}

Witt Bulter
fuente
0

El uso de Routersí mismo causará problemas que no puede superar por completo para mantener una experiencia de navegador consistente. En mi opinión, el mejor método es simplemente usar un personalizado directivey dejar que esto restablezca el desplazamiento al hacer clic. Lo bueno de esto es que si está en lo mismo urlque hace clic, la página también se desplazará hacia arriba. Esto es consistente con los sitios web normales. Lo básico directivepodría verse así:

import {Directive, HostListener} from '@angular/core';

@Directive({
    selector: '[linkToTop]'
})
export class LinkToTopDirective {

    @HostListener('click')
    onClick(): void {
        window.scrollTo(0, 0);
    }
}

Con el siguiente uso:

<a routerLink="/" linkToTop></a>

Esto será suficiente para la mayoría de los casos de uso, pero puedo imaginar algunos problemas que pueden surgir de esto:

  • No funciona universaldebido al uso dewindow
  • Impacto de poca velocidad en la detección de cambios, ya que se activa por cada clic
  • No hay forma de deshabilitar esta directiva

En realidad, es bastante fácil superar estos problemas:

@Directive({
  selector: '[linkToTop]'
})
export class LinkToTopDirective implements OnInit, OnDestroy {

  @Input()
  set linkToTop(active: string | boolean) {
    this.active = typeof active === 'string' ? active.length === 0 : active;
  }

  private active: boolean = true;

  private onClick: EventListener = (event: MouseEvent) => {
    if (this.active) {
      window.scrollTo(0, 0);
    }
  };

  constructor(@Inject(PLATFORM_ID) private readonly platformId: Object,
              private readonly elementRef: ElementRef,
              private readonly ngZone: NgZone
  ) {}

  ngOnDestroy(): void {
    if (isPlatformBrowser(this.platformId)) {
      this.elementRef.nativeElement.removeEventListener('click', this.onClick, false);
    }
  }

  ngOnInit(): void {
    if (isPlatformBrowser(this.platformId)) {
      this.ngZone.runOutsideAngular(() => 
        this.elementRef.nativeElement.addEventListener('click', this.onClick, false)
      );
    }
  }
}

Esto tiene en cuenta la mayoría de los casos de uso, con el mismo uso que el básico, con la ventaja de habilitarlo / deshabilitarlo:

<a routerLink="/" linkToTop></a> <!-- always active -->
<a routerLink="/" [linkToTop]="isActive"> <!-- active when `isActive` is true -->

comerciales, no leas si no quieres que te anuncien

Se podría hacer otra mejora para verificar si el navegador admite o no passiveeventos. Esto complicará un poco más el código y es un poco oscuro si desea implementar todo esto en sus directivas / plantillas personalizadas. Es por eso que escribí una pequeña biblioteca que puede usar para abordar estos problemas. Para tener la misma funcionalidad que anteriormente, y con el passiveevento agregado , puede cambiar su directiva a esto, si usa la ng-event-optionsbiblioteca. La lógica está dentro del click.pnboyente:

@Directive({
    selector: '[linkToTop]'
})
export class LinkToTopDirective {

    @Input()
    set linkToTop(active: string|boolean) {
        this.active = typeof active === 'string' ? active.length === 0 : active;
    }

    private active: boolean = true;

    @HostListener('click.pnb')
    onClick(): void {
      if (this.active) {
        window.scrollTo(0, 0);
      }        
    }
}
Poul Kruijt
fuente
0

Esto funcionó para mí mejor para todos los cambios de navegación, incluida la navegación hash

constructor(private route: ActivatedRoute) {}

ngOnInit() {
  this._sub = this.route.fragment.subscribe((hash: string) => {
    if (hash) {
      const cmp = document.getElementById(hash);
      if (cmp) {
        cmp.scrollIntoView();
      }
    } else {
      window.scrollTo(0, 0);
    }
  });
}
Jorg Janke
fuente
0

La idea principal detrás de este código es mantener todas las URL visitadas junto con los datos de desplazamiento respectivos en una matriz. Cada vez que un usuario abandona una página (NavigationStart), esta matriz se actualiza. Cada vez que un usuario ingresa a una nueva página (NavigationEnd), decidimos restaurar la posición Y o no dependiendo de cómo lleguemos a esta página. Si se utilizó una referencia en alguna página, nos desplazamos a 0. Si se utilizaron las funciones de retroceso / avance del navegador, nos desplazamos a Y guardado en nuestra matriz. Lo siento por mi ingles :)

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Location, PopStateEvent } from '@angular/common';
import { Router, Route, RouterLink, NavigationStart, NavigationEnd, 
    RouterEvent } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';

@Component({
  selector: 'my-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {

  private _subscription: Subscription;
  private _scrollHistory: { url: string, y: number }[] = [];
  private _useHistory = false;

  constructor(
    private _router: Router,
    private _location: Location) {
  }

  public ngOnInit() {

    this._subscription = this._router.events.subscribe((event: any) => 
    {
      if (event instanceof NavigationStart) {
        const currentUrl = (this._location.path() !== '') 
           this._location.path() : '/';
        const item = this._scrollHistory.find(x => x.url === currentUrl);
        if (item) {
          item.y = window.scrollY;
        } else {
          this._scrollHistory.push({ url: currentUrl, y: window.scrollY });
        }
        return;
      }
      if (event instanceof NavigationEnd) {
        if (this._useHistory) {
          this._useHistory = false;
          window.scrollTo(0, this._scrollHistory.find(x => x.url === 
          event.url).y);
        } else {
          window.scrollTo(0, 0);
        }
      }
    });

    this._subscription.add(this._location.subscribe((event: PopStateEvent) 
      => { this._useHistory = true;
    }));
  }

  public ngOnDestroy(): void {
    this._subscription.unsubscribe();
  }
}
Vladimir Turygin
fuente
0

window.scrollTo()no funciona para mí en Angular 5, así que he usado document.body.scrollTopcomo,

this.router.events.subscribe((evt) => {
   if (evt instanceof NavigationEnd) {
      document.body.scrollTop = 0;
   }
});
Rohan Kumar
fuente
0

Si está cargando diferentes componentes con la misma ruta, puede usar ViewportScroller para lograr lo mismo.

import { ViewportScroller } from '@angular/common';

constructor(private viewportScroller: ViewportScroller) {}

this.viewportScroller.scrollToPosition([0, 0]);
arenoso
fuente
0

window scroll top
Tanto window.pageYOffset como document.documentElement.scrollTop devuelve el mismo resultado en todos los casos. window.pageYOffset no es compatible con IE 9.

app.component.ts

import { Component, HostListener, ElementRef } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  isShow: boolean;
  topPosToStartShowing = 100;

  @HostListener('window:scroll')
  checkScroll() {

    const scrollPosition = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;

    console.log('[scroll]', scrollPosition);

    if (scrollPosition >= this.topPosToStartShowing) {
      this.isShow = true;
    } else {
      this.isShow = false;
    }
  }

  gotoTop() {
    window.scroll({ 
      top: 0, 
      left: 10, 
      behavior: 'smooth' 
    });
  }
}

app.component.html

<style>
  p {
  font-family: Lato;
}

button {
  position: fixed;
  bottom: 5px;
  right: 5px;
  font-size: 20px;
  text-align: center;
  border-radius: 5px;
  outline: none;
}
  </style>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>

<button *ngIf="isShow" (click)="gotoTop()">👆</button>
Yogesh Waghmare
fuente