網站搜尋

typescript - Angular 2 滾動到底部(聊天樣式)


我在一個 ng-for 迴圈中有一組單一元件。

我已經把一切都準備就緒,但我似乎無法弄清楚正確的方式

目前我有

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

但這並不總是有效,因為圖片會非同步地將視口向下推動。

在 Angular 2 中,滾動到聊天視窗底部的適當方式是什麼?

答案#1

我有同樣的問題,我正在使用 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(),應該就沒問題。

答案#2

這個最簡單且最好的解決方案是:

在 模板端 添加這個 #scrollMe [scrollTop]="scrollMe.scrollHeight" 簡單的東西

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

這裡是 運行示範(帶虛擬聊天應用程式)以及 完整程式碼 的連結

將適用於 Angular2 以及最高到 Angular5,如上所示的範例是使用 Angular5 完成的。

注意:

錯誤:ExpressionChangedAfterItHasBeenCheckedError

請檢查你的 CSS,這是 CSS 端的問題,而不是 Angular 端的問題。其中一位用戶 @KHAN 已經通過從 div 中移除 overflow:auto; height: 100%; 解決了這個問題。(詳細請查看對話內容)

答案#3

接受的答案會在滾動訊息時觸發,這可以避免那種情況。

你想要一個像這樣的模板。

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

答案#4

我添加了一個檢查,以查看使用者是否嘗試向上滾動。

我就把這個放在這裡,如果有人想要的話 :)

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

}

答案#5

如果你想確保在 *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" 方法

答案#6

考慮使用

.scrollIntoView()

請參見 https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

答案#7

如果你使用的是較新的 Angular 版本,以下就足夠了:

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