Как получить данные из API и отображать один элемент за раз (то есть один элемент в секунду) в angular 9? - PullRequest
0 голосов
/ 24 марта 2020

У меня есть angular служба, которая возвращает массив элементов, выбранных с сервера.

// get-item.service.ts file

getItem()

// this getItem() is a method inside my service.
// this method returns an array of JSON Objects


// component.ts file
items = []

constructor(private getItemsService: getItemsService)

ngOnInit() {
this.getItemsService.getItem().subscribe(items => {

this.items = items

}

Но внутри моего компонента я просто хочу получить sh только один элемент из выбранный массив за раз в каждом 1 се c.

//component.html file

<li *ngFor="let item of items">{{item}}</li>

Так, чтобы в моем шаблоне одновременно отображался только один элемент (т.е. один элемент в секунду).

Ответы [ 5 ]

1 голос
/ 24 марта 2020

Вы можете использовать zip(), который испускает N-й элемент, только когда все его исходные наблюдаемые издали N-й элемент:

const oneItem$ = zip(interval(1000), items$.pipe(concatAll())), 

concatAll превратит Observable<Item[]> в Observable<Item> поэтому он будет распаковывать массив и повторно отправлять каждый элемент как отдельную эмиссию.

Вы также можете использовать timer(0, 1000) вместо interval, если не хотите использовать начальную задержку в 1 с.

0 голосов
/ 25 марта 2020

Что вы можете сделать, это отобразить элементы массива (элементы) в отдельные наблюдаемые, создавая наблюдаемый второй порядок. Вы можете использовать индекс массива в сочетании с оператором delay rx js, чтобы задать различные задержки для каждого элемента.

// component.ts file

$items = this.getItemsService.getItem().pipe(
  map(items => items.map((item, i) => of(item).pipe(delay(i * 1000))))
)

Затем в своем шаблоне вы можете использовать async для подписаться как на массив $items, так и на каждого отдельного пользователя $item.

//component.html file

<li *ngFor="let $item of $items | async">{{ $item | async }}</li>
0 голосов
/ 24 марта 2020

Вы можете создать собственный оператор для этого (в качестве альтернативы всем хорошим решениям, представленным в других ответах => посмотрите на эту stackblitz demo ):


/**
 * This operator expects an array of anything and emits
 *   each element of the array at a given pace determined by
 *   a parameter that represents the interval between emissions,
 *   in ms

 * @param {n} the interval between emissions in ms 
 */
const pace = <T = number>(n: number) => <T>(
  source: Observable<Array<T>>
) => {
  return source.pipe(
    switchMap((value: Array<T>) => {
      return new Observable<T>(subscriber => {
        let counter = value.length;
        const interval = setInterval(() => {
          const v = value[value.length - counter];
          if (counter) {
            subscriber.next(v);
            counter--;
          } else {
            clearInterval(interval);
            subscriber.complete();
          }
        }, n);
      });
    })
  );
};

// emit array as single value and apply the pace operator
const arraySource = of([1, 2, 3, 4, 5]).pipe(pace(2000));

// output: 1,2,3,4,5 with interval of 2 seconds between the emissions
const subscribe = arraySource.subscribe(val => console.log(val));
0 голосов
/ 24 марта 2020

Вы можете определить отдельную переменную для отображения в шаблоне. И меняйте его значение каждую секунду на значения в массиве items. Попробуйте следующий

Компонент

items = [];
itemToRender: any;

constructor(private getItemsService: getItemsService)

ngOnInit() {
  this.getItemsService.getItem().subscribe(items => {
    this.items = items;
    this.renderItemsSequentially();
  });
}

private renderItemsSequentially() {
  let i = 0;
  let length = this.items.length - 1;
  const intervalId = setInterval(() => {
    if (i < length) {
      this.itemToRender = this.items[i];
      i++;
    } else {
      delete this.itemToRender;
      clearInterval(intervalId);
    }
  }, 1000);
}

Он использует setInterval() метод для l oop массива каждую секунду. Вы можете удалить delete this.itemToRender;, если хотите, чтобы элемент сохранялся в DOM после отображения всех элементов. Если нет, элемент будет удален после того, как все элементы будут зациклены.

И отрендерим переменную itemToRender в шаблоне. Я использую канал json, потому что вы упомянули items - список объектов.

<div *ngIf="itemToRender">Current item: {{ itemToRender | json }}</div>

Рабочий пример: Stackblitz

0 голосов
/ 24 марта 2020

Вы можете попробовать что-то похожее на это (не проверено):

(Извините, что отвечаю на свой телефон, поэтому я не уверен, что сделал опечатку)

class SomeComponent implements OnInit, OnDestroy {

  public item$: Observable<T>;
  private items: T[];
  private subscriptions: Subscription[];

  constructor(...) {
    this.items = [];
    this.subscriptions = [];
    this.item$ = interval(1000).pipe(
      filter(() => this.items.length > 0),
      map(() => this.items.shift())
    );
  }

  ngOnInit() {
    this.subscriptions.push(
      this.service.getItems().subscribe(items => {
        this.items = items;
      })
    );
  }

  ngOnDestroy() {
    for(const s of this.subscriptions) {
      s.unsubscribe();
    }
  }
}

А в вашем шаблоне:

<li>{{item$ | async}}</li>
...