Autoscroll en Angular 2


Estoy experimentando un problema con Angular 2 donde cambiar de una ruta a otra no se desplaza automáticamente a la parte superior de la nueva vista. Me doy cuenta de que Angular 1 permitió que se agregara una propiedad autoscroll a un elemento HTML, y otros habían creado un javascript simple (como window.scroll(0, 0)) para forzar a las vistas a desplazarse hacia la parte superior cuando se cargaban.

Sin embargo, no estoy seguro de cómo lograr esto con Angular 2. ¿Alguien sabe cómo lograrlo?

Author: remio, 2016-03-24

10 answers

Update

Actualmente no hay forma automática.

Ver también Error Angular 2 typescript al usar la función subscribe en un nuevo router (rc 1)

Véase también https://github.com/angular/angular/issues/6595#issuecomment-244232725

class MyAppComponent {
  constructor(router: Router) {
    router.events.subscribe(s => {
      if (s instanceof NavigationEnd) {
        const tree = router.parseUrl(router.url);
        if (tree.fragment) {
          // you can use DomAdapter
          const element = document.querySelector("#" + tree.fragment);
          if (element) { element.scrollIntoView(element); }
        }
      }
    });
  }
}

Update

En el nuevo router V3-beta.2 puede pasar un fragmento con enlaces de enrutador y navegación de enrutador

<a [routerLink]="..." fragment="top">

Debe desplazarse hasta él, pero también agrega #top a la URL (no probado yo mismo todavía)

Update

Original

Hay un tema abierto que cubre esto https://github.com/angular/angular/issues/6595

Una solución alternativa (mencionada en https://github.com/angular/angular/issues/6946 )

Inyecte el enrutador, suscríbase a los cambios de ruta e invoque el desplazamiento hacia arriba:

>= RC.x

router.changes.subscribe() => {
  window.scrollTo(0, 0);
});

Beta

router.events
.filter(e => e instanceof NavigationEnd)
.subscribe(() => {
  window.scrollTo(0, 0);
});
 17
Author: Günter Zöchbauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:20

Tiene que ver aquí: https://angular.io/docs/ts/latest/api/router/index/Router-class.html#! # events-anchor , tienes que usar "router.evento.suscribirse " desde Angular 2.0.0

Así que una buena solución para automáticamente scrool a la parte superior de toda la página es tener un AppComponent como este:

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
    constructor(private router: Router) {
        router.events.subscribe((val) => {
            if (val instanceof NavigationEnd){
                window.scrollTo(0,0);
            }
        });
    }
}
 13
Author: Thierry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-22 12:51:31

Nuevos RCs (>= RC.3) no parece exponer un changes Observable, probablemente desde entonces ha sido renombrado a events o routerEvents.

Sus documentos absolutamente "fantásticos" no parecen proporcionar ninguna información sobre lo que está haciendo qué, así que supongo que te espera un poco de Ruleta rusa allí. O lanzar una moneda o algo.

De esta respuesta , parece que el events Observable devuelve eventos con respecto al estado de navegación:

router.events.subscribe(event:Event => {
    if(event is NavigationStart) {
    }
    // NavigationEnd
    // NavigationCancel
    // NavigationError
    // RoutesRecognized   
  }
 9
Author: the_critic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:20

Tuve el mismo problema. Basándome en la respuesta de Gunter, encontré el 2 RC de Angular.1 el nuevo router no expone un Observable directamente. En su lugar tiene una propiedad changes para ese propósito. La solución para RC.1 es:

this._router.changes.subscribe(() => {
    window.scrollTo(0, 0);
}); 
 4
Author: Federico Rodriguez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-12 23:57:55

He usado si (esto.router.navegado) en el ngOnInit de cada página para determinar si usar o no la ventana.scrollTo (0, 0). Esto cubrirá la mayoría de los casos de enrutamiento a la página, dejando la posición de desplazamiento donde debería estar si hace clic en el botón de retroceso del navegador.

if(this.router.navigated) {
  window.scrollTo(0, 0);
}
 4
Author: Shane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-21 15:16:05

Solución 100% probada por mí:

constructor(router:Router){
    router.events.subscribe(() => {
        window.scrollTo(0, 0);

    }
 3
Author: RohanArihant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-15 15:22:50

Publiqué esto en el hilo de discusión , pero lo publicaré de nuevo aquí.

Mi equipo ha estado usando lo que el equipo angular usa en este repositorio en angular.io. Solo haz un servicio e inyéctalo como de costumbre. Luego, en ngAfterViewInit en cada página que desee este comportamiento, simplemente llame a esto.[scroll service variable name].scrollToTop(). Finalmente, necesitarás agregar esto a la parte superior de <body> en index.html: <div id="top-of-page"></div>

Código de servicio:

import { Injectable, Inject } from '@angular/core';
import { PlatformLocation } from '@angular/common';
import { DOCUMENT } from '@angular/platform-browser';
import {fromEvent} from 'rxjs/observable/fromEvent';

export const topMargin = 16;
/**
 * A service that scrolls document elements into view
 */
@Injectable()
export class ScrollService {

  private _topOffset: number | null;
  private _topOfPageElement: Element;

  // Offset from the top of the document to bottom of any static elements
  // at the top (e.g. toolbar) + some margin
  get topOffset() {
    if (!this._topOffset) {
      const toolbar = this.document.querySelector('md-toolbar.app-toolbar');
      this._topOffset = (toolbar && toolbar.clientHeight || 0) + topMargin;
    }
    return this._topOffset;
  }

  get topOfPageElement() {
    if (!this._topOfPageElement) {
      this._topOfPageElement = this.document.getElementById('top-of-page') || this.document.body;
    }
    return this._topOfPageElement;
  }

  constructor(
      @Inject(DOCUMENT) private document: any,
      private location: PlatformLocation) {
    // On resize, the toolbar might change height, so "invalidate" the top offset.
    fromEvent(window, 'resize').subscribe(() => this._topOffset = null);
  }

  /**
   * Scroll to the element with id extracted from the current location hash fragment.
   * Scroll to top if no hash.
   * Don't scroll if hash not found.
   */
  scroll() {
    const hash = this.getCurrentHash();
    const element: HTMLElement = hash
        ? this.document.getElementById(hash)
        : this.topOfPageElement;
    this.scrollToElement(element);
  }

  /**
   * Scroll to the element.
   * Don't scroll if no element.
   */
  scrollToElement(element: Element) {
    if (element) {
      element.scrollIntoView();

      if (window && window.scrollBy) {
        // Scroll as much as necessary to align the top of `element` at `topOffset`.
        // (Usually, `.top` will be 0, except for cases where the element cannot be scrolled all the
        //  way to the top, because the viewport is larger than the height of the content after the
        //  element.)
        window.scrollBy(0, element.getBoundingClientRect().top - this.topOffset);

        // If we are very close to the top (<20px), then scroll all the way up.
        // (This can happen if `element` is at the top of the page, but has a small top-margin.)
        if (window.pageYOffset < 20) {
          window.scrollBy(0, -window.pageYOffset);
        }
      }
    }
  }

  /** Scroll to the top of the document. */
  scrollToTop() {
    this.scrollToElement(this.topOfPageElement);
  }

  /**
   * Return the hash fragment from the `PlatformLocation`, minus the leading `#`.
   */
  private getCurrentHash() {
    return this.location.hash.replace(/^#/, '');
  }
}
 2
Author: dockleryxk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-15 21:34:17

Para aquellos de ustedes que encuentran window.scrollTo(0,0) no funciona (supongo que debido a material design sidenav pero totalmente adivinando) use el método que se encuentra aquí: Ventana Javascript / CSS.scrollTo (0,0) no funciona

 1
Author: Helzgate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:30

Estoy usando material sidenav y no pude obtener ninguna de las respuestas sugeridas para trabajar para mí. Aquí está mi solución de trabajo:

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

...

constructor(
  private router: Router,
) {
  router.events.subscribe(event => {
    if (event instanceof NavigationEnd) {
      document.querySelector('.mat-sidenav-content').scrollTop = 0;
    }
  }
}
 1
Author: Tyson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-30 18:52:31

En lugar de escribir código en cada componente, agregué el siguiente código en un solo lugar:

<router-outlet (activate)="onActivate($event)"></router-outlet>

    onActivate(e) {
        window.scrollTo(0, 0);
    }
 1
Author: Kanchan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-20 12:44:50