angular2 прокрутка вниз (стиль чата)



У меня есть набор компонентов одной ячейки в цикле ng-for.
У меня все на месте, но я не могу показаться, чтобы выяснить правильный



В настоящее время у меня есть



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


но это не работает все время как изображения асинхронно нажимаем просмотра вниз.



каков подходящий способ прокрутки до нижней части окна чата в angular2?

737   8  

8 ответов:

у меня была та же проблема, я использую 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>

вот ссылка РАБОЧАЯ ДЕМОНСТРАЦИЯ (С фиктивным приложением чата) и КОД

будет работать с Angular2, а также до 5, как показано выше демо делается в Angular5.


Примечание. :

ошибки : ExpressionChangedAfterItHasBeenCheckedError

пожалуйста, проверьте ваш css,это вопрос стороны css, а не угловой стороны , Один из пользователей @KHAN решил это, удалив overflow:auto; height: 100%; С div. (пожалуйста, проверьте разговоры для деталей)

рассмотрите возможность использования

.scrollIntoView()

см.https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

я добавил проверку, чтобы увидеть, если пользователь попытался прокрутить вверх.

Я просто оставлю это здесь, если кому интересно :)

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

}

ни один из этих ответов не является даже наполовину приличным. Принятый ответ срабатывает при прокрутке сообщений.

вы хотите такой шаблон.

<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;
}

важно здесь "последнего" переменная определяет, если вы находитесь в данный момент на последнем элементе, так что вы можете вызвать "scrollToBottom" метод

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

в угловом использовании material design sidenav мне пришлось использовать следующее:

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

Comments

    Ничего не найдено.