angular2 desplácese hasta la parte inferior (estilo de chat)


Tengo un conjunto de componentes de una sola célula dentro de un bucle ng-for. Tengo todo en su lugar, pero parece que no puedo averiguar la

Actualmente tengo un

setTimeout(() => {
  scrollToBottom();
});

Pero esto no funciona todo el tiempo, ya que las imágenes empujan hacia abajo la ventana de visualización de forma asíncrona.

¿Cuál es la forma adecuada de desplazarse hasta la parte inferior de una ventana de chat en angular2?

Author: SnareChops, 2016-02-05

8 answers

Yo tenía el mismo problema, estoy usando un AfterViewChecked y @ViewChild combinación (Angular2 beta.3).

El Componente:

import {..., AfterViewChecked, ElementRef, ViewChild, OnInit} from 'angular2/core'
@Component({
    ...
})
export class ChannelComponent implements OnInit, AfterViewChecked {
    @ViewChild('scrollMe') private myScrollContainer: ElementRef;

    ngOnInit() { 
        this.scrollToBottom();
    }

    ngAfterViewChecked() {        
        this.scrollToBottom();        
    } 

    scrollToBottom(): void {
        try {
            this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight;
        } catch(err) { }                 
    }
}

La Plantilla:

<div #scrollMe style="overflow: scroll; height: xyz;">
    <div class="..." 
        *ngFor="..."
        ...>  
    </div>
</div>

Por supuesto, esto es bastante básico. El AfterViewChecked se activa cada vez que se comprueba la vista:

Implemente esta interfaz para recibir notificaciones después de cada comprobación de la vista de su componente.

Si tiene un campo de entrada para enviar mensajes, por ejemplo, este evento es se dispara después de cada tecla (solo para dar un ejemplo). Pero si guarda si el usuario se desplazó manualmente y luego omite el scrollToBottom() debería estar bien.

 126
Author: letmejustfixthat,
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-08-17 04:54:46

La solución más simple y la mejor para esto es:

Añadir esto #scrollMe [scrollTop]="scrollMe.scrollHeight" cosa simple en Lado de la plantilla

<div style="overflow: scroll; height: xyz;" #scrollMe [scrollTop]="scrollMe.scrollHeight">
    <div class="..." 
        *ngFor="..."
        ...>  
    </div>
</div>

Aquí está el enlace para DEMOSTRACIÓN DE TRABAJO (Con la aplicación de chat ficticio) Y CÓDIGO COMPLETO

Funcionará con Angular2 y también hasta 5, como la demostración anterior se realiza en Angular5.


Nota:

Para el error: ExpressionChangedAfterItHasBeenCheckedError

Por favor, compruebe su css, es un problema de css lado, no el lado angular , Uno de los usuarios @ KHAN ha resuelto eso eliminando overflow:auto; height: 100%; de div. (por favor revise las conversaciones para más detalles)

 76
Author: Vivek Doshi,
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-11-20 12:55:14
 11
Author: Boris Yakubchik,
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-09-21 15:48:09

He añadido una comprobación para ver si el usuario intentó desplazarse hacia arriba.

Simplemente voy a dejar esto aquí si alguien lo quiere:)

<div class="jumbotron">
    <div class="messages-box" #scrollMe (scroll)="onScroll()">
        <app-message [message]="message" [userId]="profile.userId" *ngFor="let message of messages.slice().reverse()"></app-message>
    </div>

    <textarea [(ngModel)]="newMessage" (keyup.enter)="submitMessage()"></textarea>
</div>

Y el código:

import { AfterViewChecked, ElementRef, ViewChild, Component, OnInit } from '@angular/core';
import {AuthService} from "../auth.service";
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/concatAll';
import {Observable} from 'rxjs/Rx';

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

@Component({
    selector: 'app-messages',
    templateUrl: './messages.component.html',
    styleUrls: ['./messages.component.scss']
})
export class MessagesComponent implements OnInit {
    @ViewChild('scrollMe') private myScrollContainer: ElementRef;
    messages:Array<MessageModel>
    newMessage = ''
    id = ''
    conversations: Array<ConversationModel>
    profile: ViewMyProfileModel
    disableScrollDown = false

    constructor(private authService:AuthService,
                private route:ActivatedRoute,
                private router:Router,
                private conversationsApi:ConversationsApi) {
    }

    ngOnInit() {

    }

    public submitMessage() {

    }

     ngAfterViewChecked() {
        this.scrollToBottom();
    }

    private onScroll() {
        let element = this.myScrollContainer.nativeElement
        let atBottom = element.scrollHeight - element.scrollTop === element.clientHeight
        if (this.disableScrollDown && atBottom) {
            this.disableScrollDown = false
        } else {
            this.disableScrollDown = true
        }
    }


    private scrollToBottom(): void {
        if (this.disableScrollDown) {
            return
        }
        try {
            this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight;
        } catch(err) { }
    }

}
 10
Author: robert king,
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-08 00:58:46

Ninguna de estas respuestas son ni siquiera medio decentes. La respuesta aceptada se dispara mientras se desplaza por los mensajes.

Quieres una plantilla como esta.

<div #content>
  <div #messages *ngFor="let message of messages">
    {{message}}
  </div>
</div>

Entonces desea usar una anotación ViewChildren para suscribirse a los nuevos elementos de mensaje que se agregan a la página.

@ViewChildren('messages') messages: QueryList<any>;
@ViewChild('content') content: ElementRef;

ngAfterViewInit() {
  this.messages.changes.subscribe(this.scrollToBottom);
}

scrollToBottom = () => {
  try {
    this.content.nativeElement.scrollTop = this.content.nativeElement.scrollHeight;
  } catch (err) {}
}
 8
Author: denixtry,
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-10-24 17:48:42

Si desea estar seguro de que está desplazándose hasta el final después de que *ngFor haya terminado, puede usar esto.

<div #myList>
 <div *ngFor="let item of items; let last = last">
  {{item.title}}
  {{last ? scrollToBottom() : ''}}
 </div>
</div>

scrollToBottom() {
 this.myList.nativeElement.scrollTop = this.myList.nativeElement.scrollHeight;
}

Importante Aquí, la variable "last" define si actualmente se encuentra en el último elemento, por lo que puede activar el método "scrollToBottom"

 3
Author: Mert,
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
2018-04-19 17:20:37
this.contentList.nativeElement.scrollTo({left: 0 , top: this.contentList.nativeElement.scrollHeight, behavior: 'smooth'});
 2
Author: Stas Kozlov,
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
2018-03-28 07:25:24

En angular usando material design sidenav tuve que usar lo siguiente:

let ele = document.getElementsByClassName('md-sidenav-content');
    let eleArray = <Element[]>Array.prototype.slice.call(ele);
    eleArray.map( val => {
        val.scrollTop = val.scrollHeight;
    });
 0
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-04-21 22:43:56