code

Angular 2 아래로 스크롤 (채팅 스타일)

codestyles 2020. 8. 24. 08:22
반응형

Angular 2 아래로 스크롤 (채팅 스타일)


ng-for루프 내에 단일 셀 구성 요소 집합이 있습니다.

모든 것이 제자리에 있지만 적절한 것을 파악할 수없는 것 같습니다.

현재 나는

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

그러나 이미지가 비동기 적으로 뷰포트를 아래로 내리기 때문에 항상 작동하지는 않습니다.

Angular 2에서 채팅 창 하단으로 스크롤하는 적절한 방법은 무엇입니까?


나는 똑같은 문제가 있었고 a 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>

다음은 WORKING DEMO (더미 채팅 앱 포함) 및 전체 코드 링크입니다.

Angular2 및 최대 5에서도 작동합니다. 위의 데모는 Angular5에서 수행됩니다.


노트 :

오류 : ExpressionChangedAfterItHasBeenCheckedError

당신의 CSS를 확인하십시오, 그것은 CSS 측의 문제가 아닌 각도 측면, 사용자 @KHAN 중 하나는 제거하여 그것을 해결했다입니다 overflow:auto; height: 100%;에서 div. (자세한 내용은 대화를 확인하십시오)


사용자가 위로 스크롤을 시도했는지 확인하기 위해 확인을 추가했습니다.

누군가 원하면 여기에 남겨 둘 게요 :)

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

}

사용 고려

.scrollIntoView()

https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView를 참조 하십시오.


수락 된 답변은 메시지를 스크롤하는 동안 발생하므로이를 방지합니다.

이와 같은 템플릿이 필요합니다.

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

그런 다음 ViewChildren 주석을 사용하여 페이지에 추가되는 새 메시지 요소를 구독하려고합니다.

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

* 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" 메서드를 트리거 할 수 있습니다.


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

Sharing my solution, because I was not completely satisfied with the rest. My problem with AfterViewChecked is that sometimes I'm scrolling up, and for some reason, this life hook gets called and it scrolls me down even if there were no new messages. I tried using OnChanges but this was an issue, which lead me to this solution. Unfortunately, using only DoCheck, it was scrolling down before the messages were rendered, which was not useful either, so I combined them so that DoCheck is basically indicating AfterViewChecked if it should call scrollToBottom.

Happy to receive feedback.

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%

Vivek's answer has worked for me, but resulted in an expression has changed after it was checked error. None of the comments worked for me, but what I did was change the change detection strategy.

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

In angular using material design sidenav I had to use the following:

let ele = document.getElementsByClassName('md-sidenav-content');
    let eleArray = <Element[]>Array.prototype.slice.call(ele);
    eleArray.map( val => {
        val.scrollTop = val.scrollHeight;
    });

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

참고URL : https://stackoverflow.com/questions/35232731/angular-2-scroll-to-bottom-chat-style

반응형