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?
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.
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%;
dediv
. (por favor revise las conversaciones para más detalles)
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
Considere usar
.scrollIntoView()
Véase https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
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) { }
}
}
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) {}
}
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"
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'});
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;
});
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