наблюдаемый- Могу ли я пропустить / взять? - PullRequest
0 голосов
/ 29 мая 2018

Я хочу выполнить бесконечную прокрутку в моем списке объектов, они Observable<Object[]>.Передача их для обещания и await их не вариант, так как их нужно обновлять в реальном времени.

То, что я сделал, используется .map(), что работает, но проблема в том, что угловойповторный рендеринг всего списка всякий раз, когда я беру, например, 20 элементов вместо 10.

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

Вот что у меня есть

Машинопись

// Every time this happens page goes to top
// because all the items are re-rendered, not only new ones
public onScroll(): void {
  this.take += 10; // on ngOnInit() take is set to 10.
  this.sales = this.filteringObservable.map(data => {
    return _.take(data, this.take);
  });
}

HTML

<div class="sales-container" infiniteScroll [infiniteScrollDistance]="2" [infiniteScrollThrottle]="50" (scrolled)="this.onScroll()">
  <app-sales-item *ngFor="let sale of this.sales | async" [sale]="sale"></app-sales-item>
</div>

Ответы [ 3 ]

0 голосов
/ 01 июня 2018

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

Прежде всего, вы хотите использовать какую-то библиотеку, я использую ngx-infinite-scroll .

Затем вы хотите, чтобы ваш список использовал его:

<ul infiniteScroll (scrolled)="getItems()">
  <li *ngFor="let item of items; trackBy:trackByFn">{{item}}</li>
</ul>

trackByFn(i: number, item) {
  return item.<unique-key>;
}

Затем вы хотите отреагировать на прокрутку, сделайте это с помощью прослушивателя scrolled, как показано выше.

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

Вот функция, которую я использую для этого, если вы не используете API для получения дополнительных данных.результаты, которые вы можете настроить по своему вкусу.

// isReset decides whether to set offset to 0 or not
getItems(isReset = false): void {

    if (!isReset && this.items.length >= this.total) {
      return;
    }

    this.api.get<CustomResponse>('my-api-endpoint', {
      limit: 10,
      offset: isReset ? 0 : this.items.length,
    })
      .pipe(
        first(),
        tap((res) => this.total = res.totalsize || 0),
        map((res) => res.list)
      )
      .subscribe((items) => {

        // This bit prevents the same batch to be loaded into the list twice if you scroll too fast
        if (this.items.length && !isReset) {

          if (items[items.length - 1].<unique-key> === this.items[this.items.length - 1].<unique-key>) {
            return;
          }
        }

        this.items = isReset ? items : [...this.items, ...items];
      })
    );
  }

Обязательно замените <unique-key> уникальным ключом каждого элемента.

Надеюсь, это поможет.

0 голосов
/ 04 июня 2018

Вот как мы это делаем, используя Angular 5 и WebApi в качестве серверной части.Мы реализовали некоторую сортировку в наших таблицах, но вы можете использовать только часть с прокруткой и использовать ее в своем списке.Также мы берем все данные из таблиц и затем отправляем их клиентам по 10 штук, но если вам нужна скорость, вы можете пейджировать свои данные на SQL-сервере и занимать всего 10 строк за раз.Если вам нужна логика для такого рода пейджинга, просто дайте мне знать.

HTML

<div #scb id="scb" class="scrollBarClass" *ngIf="sales && sales.length" (scroll)="onScroll()">
    <div class="table table-striped table-responsive table-sm table-bordered">
        <table class="table" *ngIf="sales && sales.length">
        // here goes you table structure, headers, body and so...
        </table>
    </div>
</div>

CSS

.scrollBarClass {
  max-height: 500px;
  overflow-y: scroll;
}

Угловой компонент

import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';

@ViewChild('scb') grid: ElementRef;
scroll = false;
page = 1; 
total = 0;   

onScroll() {
    if (this.grid.nativeElement.scrollHeight - this.grid.nativeElement.scrollTop < 510 && this.scroll == false) {
        if (this.sales.length == this.total) {
            return;
        }
        let p: any;
        this.page++;
        this.scroll = true;
        this._myService.searchSales(this.page, 10, this.sortType, this.sortAD)
            .subscribe(
                (data) => { p = data['data']; this.total = data['count']},
                error => { this.errorMessage = <any>error; this.page--; },
                () => {
                    this.scroll = false;
                    Array.prototype.push.apply(this.sales, p);
                });
    }
}

Угловой сервис

searchSales(page: number, recperpage: number, sorttype: string, sortad: string) {
    let params = new HttpParams()
        .append('page', page.toString())
        .append('recperpage', recperpage.toString())
        .append('sorttype', sorttype)
        .append('sortad', sortad);
    return this._http.get<any[]>('sales/searchsales', { params: params });
}

Контроллер WebAPI

[HttpGet]
public IHttpActionResult searchsales(int page, int recperpage, string sorttype, string sortad)
{
    try
    {
        var count = 0;
        var r = _salesService.SearchSales(sorttype, sortad, ref count);
        return Ok(new { data=r.Skip((page - 1) * recperpage).Take(recperpage).ToList(), count });
    }
    catch (Exception e)
    {
        return InternalServerError(e);
    }
}

Сервис WebAPI

public List<Sales> SearchSales(string sorttype, string sortad, ref int count)
{
    var query = "";

    query = " select * FROM Sales "+
            " ORDER BY " + ((sorttype == null) ? "DateOfSale" : sorttype) + " " + ((sortad == null) ? "DESC" : sortad);
    var result = SQLHelper.ExecuteReader(query);
    count = result.Count;
    return result;
}
0 голосов
/ 01 июня 2018

Вот код, который я использую для этого сценария:

private filterModel: FilterModel = new FilterModel();
private page: number = 0;

items: ResultModel[] = [];

public onScroll(): void {

  if (this.scrolling) {
    return;
  }
  this.scrolling = true;
  this.page++;

  this.service.loadStuff(this.filterModel, this.page).subscribe(items => {
      !items || !items.length
        ? this.page--
        : items.forEach(item => this.items.push(item));
      this.scrolling = false;
    },
    () => this.scrolling = false);
}

Где filterModel - это объект, содержащий данные о том, что загружать и как его загружать - размер страницы, фильтры свойств и т. Д..

Если вы хотите выполнить это более RxJs способом, вы можете попробовать что-то вроде этого (не проверено, приспособиться к вашим потребностям):

private filterModel: FilterModel = new FilterModel();
private filter$ = new Subject<FilterModel>();

private page: number = 0;
private page$ = new Subject<number>();

private cache: ResultModel[][] = [];

items$: Observable<ResultModel>;

ngOnInit() {

  const filter$ = this.filter$.pipe(
    debounceTime(1000),
    distinctUntilChanged(),
    tap(() => this.cache = []),
    map(model => ({ model: this.filterModel = model, page: 0 }))
  );
  const page$ = this.page$.pipe(
    map(pageNumber => ({ model: this.filterModel, page: pageNumber }))
  );

  this.items$ = merge(page$, filter$).pipe(
    startWith({ model: this.filterModel, page: this.page }),
    tap(() => this.scrolling = true)
    mergeMap(({ model: FilterModel, page: number }) => 
      this.service.loadStuff(model, page).pipe(
        tap(response => this.cache[page] = response.items)
      )
    ),
    tap(response => ... ), // Do whatever else with your response here
    map(() => _.flatMap(this.cache)),
    tap(() => this.scrolling = false)
  );
}

onScroll() {
  this.page$.next(this.page++);
}

onFilter(model: FilterModel){
  this.filter$.next(model);
}

Надеюсь, что это направляет васпуть:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...