typescript - Angular 2 Gulir ke bawah (Gaya chat)
Saya memiliki satu set komponen sel tunggal dalam sebuah loop ng-for.
Saya memiliki semuanya di tempatnya tetapi saya sepertinya tidak bisa menemukan yang tepat
Saat ini saya memiliki
setTimeout(() => {
scrollToBottom();
});
Tapi ini tidak selalu berhasil karena gambar secara asinkron mendorong viewport ke bawah.
Apa cara yang tepat untuk menggulir ke bagian bawah jendela chat di Angular 2?
Jawaban #1
Saya mengalami masalah yang sama, saya menggunakan kombinasi AfterViewChecked dan @ViewChild (Angular2 beta.3).
Komponen:
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) { }
}
}
Template:
<div #scrollMe style="overflow: scroll; height: xyz;">
<div class="..."
*ngFor="..."
...>
</div>
</div>
Tentu saja ini cukup dasar. AfterViewChecked dipicu setiap kali tampilan diperiksa:
Terapkan antarmuka ini untuk diberitahu setelah setiap pemeriksaan pada tampilan komponen Anda.
Jika Anda memiliki bidang input untuk mengirim pesan misalnya, event ini dipicu setelah setiap keyup (hanya untuk memberikan contoh). Tetapi jika Anda menyimpan apakah pengguna menggulir secara manual dan kemudian melewati scrollToBottom() seharusnya Anda baik-baik saja.
Jawaban #2
Solusi termudah dan terbaik untuk ini adalah :
Tambahkan hal sederhana ini #scrollMe [scrollTop]="scrollMe.scrollHeight" di sisi Template
<div style="overflow: scroll; height: xyz;" #scrollMe [scrollTop]="scrollMe.scrollHeight">
<div class="..."
*ngFor="..."
...>
</div>
</div>
Berikut adalah tautan untuk DEMO BERFUNGSI (Dengan aplikasi chat contoh) DAN KODE LENGKAP
Akan bekerja dengan Angular2 dan juga hingga 5, seperti demo di atas dilakukan di Angular5.
Catatan :
Untuk kesalahan:
ExpressionChangedAfterItHasBeenCheckedErrorSilakan periksa CSS Anda, ini adalah masalah dari sisi CSS, bukan sisi Angular. Salah satu pengguna @KHAN telah menyelesaikannya dengan menghapus
overflow:auto; height: 100%;daridiv. (silakan periksa percakapan untuk detailnya)
Jawaban #3
Jawaban yang diterima berjalan saat menggulir pesan, ini mencegah hal itu.
Anda menginginkan templat seperti ini.
<div #content>
<div #messages *ngFor="let message of messages">
{{message}}
</div>
</div>
Kemudian Anda ingin menggunakan anotasi ViewChildren untuk berlangganan elemen pesan baru yang ditambahkan ke halaman.
@ViewChildren('messages') messages: QueryList<any>;
@ViewChild('content') content: ElementRef;
ngAfterViewInit() {
this.scrollToBottom();
this.messages.changes.subscribe(this.scrollToBottom);
}
scrollToBottom = () => {
try {
this.content.nativeElement.scrollTop = this.content.nativeElement.scrollHeight;
} catch (err) {}
}
Jawaban #4
Saya menambahkan pemeriksaan untuk melihat apakah pengguna mencoba menggulir ke atas.
Aku hanya akan meninggalkan ini di sini jika ada yang mau :)
<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>
dan kodenya:
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) { }
}
}
Jawaban #5
Jika Anda ingin yakin, bahwa Anda menggulir ke akhir setelah *ngFor selesai, Anda dapat menggunakan ini.
<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;
}
Penting di sini, variabel "last" menentukan apakah Anda saat ini berada di item terakhir, sehingga Anda dapat memicu metode "scrollToBottom"
Jawaban #6
Pertimbangkan untuk menggunakan
.scrollIntoView()
Lihat https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
Jawaban #7
Jika Anda menggunakan versi Angular terbaru, berikut ini sudah cukup:
<div #scrollMe style="overflow: scroll; height: xyz;" [scrollTop]="scrollMe.scrollHeight">
<div class="..."
*ngFor="..."
...>
</div>
</div>
