Прокрутите вниз с помощью cdk-virtual-scroll (Angular 8)

Цель

Отображение списка сообщений и прокрутка вниз при получении нового сообщения, даже если я нахожусь наверху. Я бы хотел полностью прокрутить низ даже с элементами разной высоты.

Проблема

С помощью виртуальной прокрутки я должен установить свойство [itemSize], но для меня это не статическое значение:

  • Когда сообщение слишком длинное для одной строки, оно разбивается на несколько, поэтому его высота изменяется.
  • У меня есть разные типы сообщений с разной высотой (например, системные сообщения).

Кроме того, я использую ng-content, чтобы вставить кнопку родительского элемента для загрузки предыдущих сообщений. Я вижу, что когда вызывается _scrollToBottom, вместо того, чтобы увести меня вниз, он уносит меня немного выше. Я подозреваю, что это из-за разной высоты элементов внутри virtual-scroll.

Я прочитал эту проблему стратегии автоматического изменения размера прокрутки из Angular: https://github.com/angular/components/issues/10113;, но я не уверен, что это решит мою проблему.

Любая идея о том, что я могу сделать, будет приветствоваться.

Тестовое задание

Codesandbox: https://codesandbox.io/s/angular-virtual-scroll-biwn6

  • Когда сообщения загружены, прокрутите вверх.
  • Отправить сообщение. (Когда новое сообщение загружено, вместо прокрутки вниз виртуальная прокрутка останавливается немного выше)
  • Повторить

Видео с ошибкой: https://gofile.io/d/8NG9HD


Решение

Решение, данное Гоуравом Гаргом, работает. Просто дважды выполнив функцию прокрутки.

Я делаю это сейчас:


  private _scrollToBottom() {
    setTimeout(() => {
      this.virtualScrollViewport.scrollTo({
        bottom: 0,
        behavior: 'auto',
      });
    }, 0);
    setTimeout(() => {
      this.virtualScrollViewport.scrollTo({
        bottom: 0,
        behavior: 'auto',
      });
    }, 50);
  }

Я считаю, что это не очень элегантно, но работает нормально.


person adrisons    schedule 20.11.2020    source источник
comment
Какую версию angular cdk вы используете?   -  person Gourav Garg    schedule 23.11.2020
comment
@GouravGarg "@angular/cdk": "^8.2.3"   -  person adrisons    schedule 24.11.2020


Ответы (3)


Вы можете использовать, если используете cdk ›7

this.virtualScrollViewport.scrollToIndex(messages.length-1);

Поскольку это переместится в начало последнего элемента. Вам нужно вызвать scrollIntoView для этого элемента.

this.virtualScrollViewport.scrollToIndex(this.numbers.length - 1);
setTimeout(() => {
  const items = document.getElementsByClassName("list-item");
  items[items.length - 1].scrollIntoView();
}, 10);
<cdk-virtual-scroll-viewport #virtualScroll style="height: 500px" itemSize="90">
  <ng-container *cdkVirtualFor="let n of numbers">
    <li class="list-item"> {{n}} </li>
  </ng-container>
</cdk-virtual-scroll-viewport>

Я обновил вашу песочницу

person Gourav Garg    schedule 23.11.2020
comment
Спасибо за ваш ответ! Он ведет себя так же, как scrollToIndex. Он не доходит до конца, оставляет последнее сообщение ниже. - person adrisons; 24.11.2020
comment
Что ж, можно взломать. Просто добавьте класс для всех своих элементов и после вышеуказанного события найдите последний элемент и используйте прокрутку для этого элемента в соответствии с @ adarsh-thakur stackblitz.com/edit/ - person Gourav Garg; 25.11.2020
comment
Пробовал, не работает. Из-за виртуальной прокрутки отображаются только элементы X, поэтому при использовании getElementsByClassName извлекаются только они, и выполняется прокрутка до последнего отображаемого элемента. Спасибо за Ваш ответ - person adrisons; 25.11.2020
comment
Здесь обновлена ​​одна песочница codeandbox.io/s / - person Gourav Garg; 25.11.2020
comment
Я не вижу разницы: / пожалуйста, следуйте шагам тестирования из моего описания - person adrisons; 25.11.2020
comment
Да, я следил за твоими шагами и работал так, как тебе нужно. - person Gourav Garg; 25.11.2020
comment
Попробуйте отправить несколько сообщений, прокрутите вверх и отправьте другое сообщение. У меня свиток не доходит до конца - person adrisons; 25.11.2020
comment
Я обновил этот codeandbox.io/s/angular-virtual-scroll-forked-hruxe - person Gourav Garg; 26.11.2020
comment
Что ж, работает! Решением было дважды вызвать функцию прокрутки с разным временем в setTimeout. - person adrisons; 26.11.2020

Есть альтернатива cdk scroll.

Вот типовая версия кода для перехода к заданному элементу HTML. Его можно использовать как служебную функцию в angular или как функцию в javascript.

scroll(el: HTMLElement, behaviour: any = "smooth", block: any = "start", inline: any = "nearest") {
    el.scrollIntoView({ behavior: behaviour, block: block, inline: inline })
  }

Образец HTML:

<div class="scroll-to-top" [ngClass]="{'show-scrollTop': windowScrolled}">
    Back to top<button mat-button mat-icon-button (click)="scrollToTop()">
        <mat-icon>keyboard_arrow_up</mat-icon>
    </button>
</div>

Пример кода компонента Typescript:

@HostListener("window:scroll")
  onWindowScroll() {
    if (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop > 100) {
      this.windowScrolled = true;
    }
    else if (this.windowScrolled && window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop < 10) {
      this.windowScrolled = false;
    }
  }
  scrollToTop() {
    (function smoothscroll() {
      var currentScroll = document.documentElement.scrollTop || document.body.scrollTop;
      if (currentScroll > 0) {
        window.requestAnimationFrame(smoothscroll);
        window.scrollTo(0, currentScroll - (currentScroll / 8));
      }
    })();
  }
person Srinath Kamath    schedule 30.11.2020
comment
Мой cdk размещен внутри компонента, который не использует прокрутку окна. Думаю, это было бы хорошим решением для чего-то вроде блога. Спасибо! - person adrisons; 30.11.2020

Используйте scrollIntoView() для прокрутки до конца virtualScrollViewport

this.virtualScrollViewport.scrollIntoView(false);

обратитесь к https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

person Adarsh Thakur    schedule 24.11.2020