根据事件从Observable中获取N个值,直到其完成为止.延迟加载多选列表 [英] Take N values from Observable until its complete based on an event. Lazy loading a multi select list

查看:64
本文介绍了根据事件从Observable中获取N个值,直到其完成为止.延迟加载多选列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm new to rxjs, and I'm developing an angular multiselect list component that should render a long list of values (500+). I'm rendering the list based on an UL, I'm iterating over an observable that will render the LI's. I'm thinking about my options to avoid impacting the performance by rendering all the elements at once. But I don't know whether this is possible or not, and if it's possible what is the best operator to use.

建议的解决方案:

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

  • On init I load all the data into an Observable. (src) and I'll take 100 elements from it and will put them on the target observable (The one that will be used to render the list)
  • Everytime that the user reaches the end of the list (the scrollEnd event fires) I'll load 100 elements more, until there are no more values in the src observable.

scrollEnd事件将触发目标可观察到的新值.

The emission of new values in the target observable will be triggered by the scrollEnd event.

在下面找到我的代码,我仍然需要实现建议的解决方案,但此时此刻我被困住了.

Find my code below, I still need to implement the proposed solution, but I'm stuck at this point.

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

EDIT: I'm implementing @martin solution, but I'm still not able to make it work in my code. My first step was to replicate it in the code, to get the logged values, but the observable is completing immediately without producing any values. Instead of triggering an event, I've added a subject. Everytime the scrollindEnd output emits, I will push a new value to the subject. The template has been modified to support this.

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组件.

NOTE: The scrollingFinished event is being triggered by the scrollTracker directive that holds the tracking logic. I'm bubbling the event from multiselect-list to the multiselect component.

提前谢谢!

推荐答案

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

This example generates an array of 450 items and then splits them into chunks of 100. It first dumps the first 100 items and after every button click it takes another 100 and appends it to the previous results. This chain properly completes after loading all data.

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

I think you should be able to take this and use to for your problem. Just instead of button clicks use a Subject that will emit every time user scrolls to the bottom:

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

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

If you're concerned about performance you should use trackBy for *ngFor to avoid re-rendering existing DOM elements but I guess you already know that.

这篇关于根据事件从Observable中获取N个值,直到其完成为止.延迟加载多选列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆