我有一组单元格组件,在ng-for
循环中。
我已经准备就绪,但似乎无法找出正确的方法。
目前我有:
setTimeout(() => {
scrollToBottom();
});
但是这并不总是有效的,因为图像异步地向下推动视口。
在 Angular 2 中,滚动到聊天窗口底部的适当方法是什么?
我有一组单元格组件,在ng-for
循环中。
我已经准备就绪,但似乎无法找出正确的方法。
目前我有:
setTimeout(() => {
scrollToBottom();
});
但是这并不总是有效的,因为图像异步地向下推动视口。
在 Angular 2 中,滚动到聊天窗口底部的适当方法是什么?
我遇到了同样的问题,我正在使用 AfterViewChecked
和 @ViewChild
的组合(Angular2 beta.3)。
该组件:
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) { }
}
}
模板:
<div #scrollMe style="overflow: scroll; height: xyz;">
<div class="..."
*ngFor="..."
...>
</div>
</div>
当然,这很基础。每次检查视图时,AfterViewChecked
都会触发:
实现此接口以在组件视图的每次检查后收到通知。
例如,如果您有一个用于发送消息的输入字段,则每次按键松开时都会触发此事件(仅供参考)。但是,如果保存了用户是否手动滚动并跳过了scrollToBottom()
,则应该没问题。
最简单和最好的解决方案是:
在模板端添加这个#scrollMe [scrollTop]="scrollMe.scrollHeight"
简单的代码。
<div style="overflow: scroll; height: xyz;" #scrollMe [scrollTop]="scrollMe.scrollHeight">
<div class="..."
*ngFor="..."
...>
</div>
</div>
这是一个包含虚拟聊天应用的 演示链接 和完整代码 FULL CODE,支持 Angular2 到 5 版本。
注意:
如果出现错误:
ExpressionChangedAfterItHasBeenCheckedError
请检查你的 CSS,这是 CSS 方面的问题,而不是 Angular 的问题。其中一位名叫 @KHAN 的用户通过从
div
中删除overflow:auto; height: 100%;
解决了这个问题(请查看对话以获取详细信息)。
被接受的答案在滚动消息时触发,这样可以避免那种情况。
你想要一个像这样的模板。
<div #content>
<div #messages *ngFor="let message of messages">
{{message}}
</div>
</div>
那么您需要使用ViewChildren注释来订阅添加到页面中的新消息元素。
@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) {}
}
我添加了一个检查,以查看用户是否尝试向上滚动。
如果有人需要,我就把它放在这里 :)
<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>
并且代码:
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) { }
}
}
如果你想确定在*ngFor完成后滚动到底部,可以使用以下方法。
<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;
}
重要的是,"last" 变量定义了您当前是否在最后一个项目,因此您可以触发 "scrollToBottom" 方法。
@ViewChild('content') content: ElementRef;
来实现建议的ScrollToBottom。 - Pianomanthis.contentList.nativeElement.scrollTo({left: 0 , top: this.contentList.nativeElement.scrollHeight, behavior: 'smooth'});
如果你使用的是 Angular 的最新版本,则只需要以下内容:
<div #scrollMe style="overflow: scroll; height: xyz;" [scrollTop]="scrollMe.scrollHeight>
<div class="..."
*ngFor="..."
...>
</div>
</div>
const element = document.getElementById('box');
element.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'nearest' });
这个问题的标题提到了“聊天风格”的滚动到底部,这也是我所需要的。然而,这些答案都没有真正地满足我的需求,因为我真正想做的是在子元素添加或删除时滚动到我的div的底部。最终,我使用了这个非常简单的指令来利用MutationObserver API。
@Directive({
selector: '[pinScroll]',
})
export class PinScrollDirective implements OnInit, OnDestroy {
private observer = new MutationObserver(() => {
this.scrollToPin();
});
constructor(private el: ElementRef) {}
ngOnInit() {
this.observer.observe(this.el.nativeElement, {
childList: true,
});
}
ngOnDestroy() {
this.observer.disconnect();
}
private scrollToPin() {
this.el.nativeElement.scrollTop = this.el.nativeElement.scrollHeight;
}
}
height
和overflow
规则。