top component bottom scroll typescript angular

scroll - component - angular2 desplazarse hacia abajo(estilo de chat)



scroll to element angular 5 (11)

Agregué un cheque para ver si el usuario intentó desplazarse hacia arriba.

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

Tengo un conjunto de componentes de celda única dentro de un bucle ng-for. Tengo todo en su lugar pero parece que no puedo entender lo correcto

Actualmente tengo un

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

Pero esto no funciona todo el tiempo ya que las imágenes empujan asincrónicamente la ventana hacia abajo.

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


Compartiendo mi solución, porque no estaba completamente satisfecho con el resto. Mi problema con AfterViewChecked es que a veces me AfterViewChecked hacia arriba y, por alguna razón, se llama a este gancho de vida y me desplaza hacia abajo incluso si no había mensajes nuevos. Intenté usar OnChanges pero this fue un problema que me llevó a this solución. Desafortunadamente, usando solo DoCheck , se desplazaba hacia abajo antes de que se DoCheck los mensajes, lo que tampoco fue útil, por lo que los combiné para que DoCheck básicamente indique AfterViewChecked si debería llamar a scrollToBottom .

Feliz de recibir comentarios.

export class ChatComponent implements DoCheck, AfterViewChecked { @Input() public messages: Message[] = []; @ViewChild(''scrollable'') private scrollable: ElementRef; private shouldScrollDown: boolean; private iterableDiffer; constructor(private iterableDiffers: IterableDiffers) { this.iterableDiffer = this.iterableDiffers.find([]).create(null); } ngDoCheck(): void { if (this.iterableDiffer.diff(this.messages)) { this.numberOfMessagesChanged = true; } } ngAfterViewChecked(): void { const isScrolledDown = Math.abs(this.scrollable.nativeElement.scrollHeight - this.scrollable.nativeElement.scrollTop - this.scrollable.nativeElement.clientHeight) <= 3.0; if (this.numberOfMessagesChanged && !isScrolledDown) { this.scrollToBottom(); this.numberOfMessagesChanged = false; } } scrollToBottom() { try { this.scrollable.nativeElement.scrollTop = this.scrollable.nativeElement.scrollHeight; } catch (e) { console.error(e); } } }

chat.component.html

<div class="chat-wrapper"> <div class="chat-messages-holder" #scrollable> <app-chat-message *ngFor="let message of messages" [message]="message"> </app-chat-message> </div> <div class="chat-input-holder"> <app-chat-input (send)="onSend($event)"></app-chat-input> </div> </div>

chat.component.sass

.chat-wrapper display: flex justify-content: center align-items: center flex-direction: column height: 100% .chat-messages-holder overflow-y: scroll !important overflow-x: hidden width: 100% height: 100%



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


La respuesta aceptada se dispara mientras se desplaza por los mensajes, esto evita eso.

Quieres una plantilla como esta.

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

Luego, desea utilizar una anotación ViewChildren para suscribirse a los nuevos elementos de mensaje que se agreguen 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) {} }


La respuesta de Vivek me funcionó, pero resultó en una expresión que cambió después de que se verificó el error. Ninguno de los comentarios funcionó para mí, pero lo que hice fue cambiar la estrategia de detección de cambios.

import { Component, ChangeDetectionStrategy } from ''@angular/core''; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, selector: ''page1'', templateUrl: ''page1.html'', })


Si quiere estar seguro de que se desplazará hasta el final después de * ngFor, 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 "último" define si está actualmente en el último elemento, por lo que puede activar el método "scrollToBottom"


Tuve el mismo problema, estoy usando una combinación AfterViewChecked y @ViewChild (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. AfterViewChecked activa cada vez que se verifica la vista:

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

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


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

Agregue esta cosa simple #scrollMe [scrollTop]="scrollMe.scrollHeight" en el lado de la Plantilla

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

Aquí está el enlace para la DEMOSTRACIÓN DE TRABAJO (con 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 :

Por error: ExpressionChangedAfterItHasBeenCheckedError

Verifique su css, es un problema del lado css, no del lado angular, uno de los usuarios @KHAN lo resolvió eliminando el overflow:auto; height: 100%; overflow:auto; height: 100%; de div . (por favor revise las conversaciones para más detalles)


const element = document.getElementById(''box''); element.scrollIntoView({ behavior: ''smooth'', block: ''end'', inline: ''nearest'' });


this.contentList.nativeElement.scrollTo({left: 0 , top: this.contentList.nativeElement.scrollHeight, behavior: ''smooth''});