javascript-根据事件从Observable中获取N个值,直到其完成为止.延迟加载多选列表

我是rxjs的新手,并且正在开发一个有角度的多选列表组件,该组件应呈现一长串值(500).
我正在根据UL渲染列表,正在遍历可渲染LI的可观察对象.
我正在考虑我的选择,以避免通过一次渲染所有元素来影响性能.但是我不知道这是否可行,是否有可能使用最佳运算符.

建议的解决方案:

>在初始化时,我将所有数据加载到一个Observable中. (src),然后从中提取100个元素,并将其放在可观察的目标中(将用于呈现列表的元素)
>每当用户到达列表的末尾时(scrollEnd事件将触发),我将再加载100个元素,直到src中的可观察值不再更多.
> scrollEnd事件将触发目标可观察到的新值的发射.

在下面找到我的代码,我仍然需要实现建议的解决方案,但是我仍然停留在这一点上.

编辑:我正在实现@martin解决方案,但我仍然无法使其在我的代码中工作.我的第一步是将其复制到代码中,以获取记录的值,但是可观察对象将立即完成而不会产生任何值.
我没有触发事件,而是添加了一个主题.每当scrollindEnd输出发出时,我都会向主题推送一个新值.模板已被修改以支持此操作.

multiselect.component.ts

import { Component, AfterViewInit } from '@angular/core';
import { zip, Observable, fromEvent, range } from 'rxjs';
import { map, bufferCount, startWith, scan } from 'rxjs/operators';
import { MultiSelectService, ProductCategory } from './multiselect.service';

@Component({
  selector: 'multiselect',
  templateUrl: './multiselect.component.html',
  styleUrls: ['./multiselect.component.scss']
})
export class MultiselectComponent implements AfterViewInit {

  SLICE_SIZE = 100;
  loadMore$: Observable<Event>;
  numbers$= range(450);

  constructor() {}


  ngAfterViewInit() {
    this.loadMore$= fromEvent(document.getElementsByTagName('button')[0], 'click');

    zip(
      this.numbers$.pipe(bufferCount(this.SLICE_SIZE)),
      this.loadMore$.pipe(),
    ).pipe(
      map(results => console.log(results)),
    ).subscribe({
      next: v => console.log(v),
      complete: () => console.log('complete ...'),
    });
  }

}

multiselect.component.html

<form action="#" class="multiselect-form">
  <h3>Categories</h3>
  <input type="text" placeholder="Search..." class="multiselect-form--search" tabindex="0"/>
  <multiselect-list [categories]="categories$| async" (scrollingFinished)="lazySubject.next($event)">
  </multiselect-list>
  <button class="btn-primary--large">Proceed</button>
</form>

multiselect-list.component.ts

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'multiselect-list',
  templateUrl: './multiselect-list.component.html'
})
export class MultiselectListComponent {
  @Output() scrollingFinished = new EventEmitter<any>();
  @Input() categories: Array<string> = [];

  constructor() {}

  onScrollingFinished() {
    this.scrollingFinished.emit(null);
  }
}

multiselect-list.component.html

<ul class="multiselect-list" (scrollingFinished)="onScrollingFinished($event)">
  <li *ngFor="let category of categories; let idx=index" scrollTracker class="multiselect-list--option">
    <input type="checkbox" id="{{ category }}" tabindex="{{ idx + 1 }}"/>
    <label for="{{ category }}">{{ category }}</label>
  </li>
</ul>

注意:scrollingFinished事件由保存跟踪逻辑的scrollTracker指令触发.我正在将事件从multiselect-list冒泡到multiselect组件.

提前致谢!

解决方法:

此示例生成一个由450个项目组成的数组,然后将它们分成100个大块.它首先转储前100个项目,并且在每次单击按钮后将再提取100个项目并将其附加到以前的结果中.加载所有数据后,此链正确完成.

我认为您应该能够将此并用于解决您的问题.只是单击按钮而不是单击按钮,而是使用一个主题,该主题将在用户每次滚动到底部时发出:

import { fromEvent, range, zip } from 'rxjs'; 
import { map, bufferCount, startWith, scan } from 'rxjs/operators';

const SLICE_SIZE = 100;

const loadMore$= fromEvent(document.getElementsByTagName('button')[0], 'click');
const data$= range(450);

zip(
  data$.pipe(bufferCount(SLICE_SIZE)),
  loadMore$.pipe(startWith(0)),
).pipe(
  map(results => results[0]),
  scan((acc, chunk) => [...acc, ...chunk], []),
).subscribe({
  next: v => console.log(v),
  complete: () => console.log('complete'),
});

现场演示:https://stackblitz.com/edit/rxjs-au9pt7?file=index.ts

如果您担心性能,则应该对* ngFor使用trackBy以避免重新呈现现有的DOM元素,但是我想您已经知道了.

上一篇:javascript – 使用RxJS和twitter-stream-api模块订阅流


下一篇:javascript – RxJs Observables:在一些更多的异步请求之后运行retry