javascript - 从 Observable 中获取 N 个值,直到它基于一个事件完成。延迟加载多选列表

标签 javascript angular ecmascript-6 rxjs lazy-loading

我是 rxjs 的新手,我正在开发一个 Angular 多选列表组件,它应该呈现一长串值 (500+)。 我正在根据 UL 渲染列表,我正在迭代将渲染 LI 的可观察对象。 我正在考虑我的选择,以避免通过一次渲染所有元素来影响性能。但我不知道这是否可能,如果可能的话,最好使用什么运算符。

建议的解决方案:

  • 在初始化时,我将所有数据加载到一个 Observable 中。 (src) 我将从中取出 100 个元素并将它们放在目标可观察对象上(将用于呈现列表的那个)
  • 每当用户到达列表末尾(触发 scrollEnd 事件)时,我将再加载 100 个元素,直到 src observable 中没有更多值为止。

  • 目标可观察对象中新值的发射将由 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>

多选列表.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 指令触发。我将事件从多选列表冒泡到多选组件。

提前致谢!

最佳答案

此示例生成一个包含 450 个项目的数组,然后将它们拆分为 100 的 block 。它首先转储前 100 个项目,在每次单击按钮后,它又获取另一个 100 并将其附加到先前的结果中。此链在加载所有数据后正确完成。

我认为您应该能够接受并使用它来解决您的问题。只需使用 Subject 代替按钮点击,每次用户滚动到底部时都会发出:

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 - 从 Observable 中获取 N 个值,直到它基于一个事件完成。延迟加载多选列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54852013/

相关文章:

javascript - typescript /Angular 传递值?

angular - 找不到 Ionic4 Angular7 ngx-translate 管道

ecmascript-6 - 使用 TypeScript 时如何在单个文件组件中导入 Vue?

javascript - 在 JavaScript 类中抛出错误是否被认为是不好的做法?

javascript - 在 React/Redux 中更改 url

javascript - 在播放器上的浏览器上播放 wav 字节数据

angular - 为什么拦截器获取不到路由参数?

javascript - 在对象数组中,如何更新(而不仅仅是查找)属性 X 等于 Y 的对象?

javascript - 检测上传对话框中的打开按钮

c# - 很难找到在正则表达式中插入 "word exclusion"的位置