将对象转换为数组然后每隔几秒显示一个元素

Convert Object to Array then display one element every few seconds

我像这样在我的服务中调用 API

getItems(): Observable<{}> {
    return this.http.get(<path>)
    .pipe(
      pluck('items'),
    );
  }

returns 数据的形状:

items: Object { key: value, key: value, key: value ...}

我像这样在我的组件中订阅了这个

items$!: any;
constructor(private readonly service: Service) { }

  ngOnInit(): void {
    this.service.getItems().subscribe(items => this.items$ = items);
  }

并像这样在我的模板中显示它们

<div *ngFor="let item of items$ | keyvalue">
    <mat-card>
        <mat-card-header>
          {{item.key}}
        </mat-card-header>
        <mat-card-content>
          {{item.value}}
        </mat-card-content>
    </mat-card>
</div>

但是,我真正想要的是每隔几秒左右一次循环显示这些项目,而不是一次显示所有项目。我也想只进行一次 http 调用来完成此操作。我环顾四周,发现了解决方案的一些细节,但是,它通常至少涉及将对象转换为数组,然后可能使用某种 Subject 来观察订阅的原始可观察对象?这样做的干净方法是什么?谢谢。

一种解决方案是将数据从您的 API 转换为具有以下结构的数组:

const apiData = [{ a: 1 }, { b: 2 }, { c: 3 }, { d: 4 }, { e: 5 }, { f: 6 }];

接下来您可以使用以下使用 bufferCount, concatMap and delay 运算符的代码

data = [];

from(apiData)
  .pipe(
    bufferCount(1),
    concatMap(objs => of(objs).pipe(delay(3000)))
  )
  .subscribe(e => {
    this.data.push(e);
  });

在模板上

<div *ngFor="let item of data">
    {{item | json}}    <-- tweak around with it to get exact details you need -->
</div>

创建一个每 5 秒发出一个值并与所有订阅者共享相同值的流。


item$: Observable<Item>;

ngOnInit(): void { 
  this.item$ = this.service.getItems().pipe(
    switchMap(items => zip(
      from(items),
      timer(0,5000)
    )),
    map(([x,_]) => x),
    share()
  );
}

订阅流。异步管道是 angular

的最佳方式
<div>
    <mat-card>
        <mat-card-header>
          {{(item$ | async).key}}
        </mat-card-header>
        <mat-card-content>
          {{(item$ | async).value}}
        </mat-card-content>
    </mat-card>
</div>

更新 - 使用自定义运算符

function intervalArray<T>(milliseconds = 1000): OperatorFunction<T[], T> {

  return s => s.pipe(
    concatMap((a: T[]) => zip(
      from(a),
      timer(0, milliseconds)
    ).pipe(
      map(([x,_]) => x)
    ))
  );
  
}

ngOnInit(): void { 
  this.item$ = this.service.getItems().pipe(
    intervalArray(5000),
    share()
  );
}

谢谢尼古拉斯先生。你帮了大忙。我将 Mrk 的回答标记为答案,因为我基本上使用了他们的代码。以防万一有人对是什么解决了我在评论中提到的几件事感到好奇,并希望在上下文中查看整个解决方案,这就是我所做的全部内容

//in the service
getItems(): Observable<{}> {
    return this.http.get(<URL>)
    .pipe(
      pluck('items'),
    );
  }

//in the template, this change stopped the initial null issue I was having
<div *ngIf="item$ | async as state">
  <mat-card>
      <mat-card-header>
        {{state.currencyPair}}
      </mat-card-header>
      <mat-card-content>
        {{state.exchangeRate}}
      </mat-card-content>
  </mat-card>
</div>

//in the component
  item$!: Observable<Item>;
  constructor(private readonly service: Service) { }

  ngOnInit(): void {
    this.item$ = this.service.getItems().pipe(
      switchMap(items => zip(
        from(this.objectToArray(items)),
        timer(0,5000)
      )),
      map(([x,_]) => x),
      shareReplay(1)
    );
  }

  objectToArray(object: {}) {
    let items: Item[] = [];
    for (const [key, value] of Object.entries(object)) { 
      items.push({ key : key, value : value })
    }
    return items;
  }

//item.ts
export interface Item {
    key: string;
    value: any;
}

现在的问题是,在刷新 objectToArray 后得到一个未定义的对象显然是 API 的问题,因为我的免费计划不支持 https 加密,哈哈。我以为我通过从 URL 中取出 s 解决了这个问题,但是 耸耸肩 .

Nicholas,如果您想分享,我真的很想知道您的代码在上下文中如何工作。无论如何,我需要更深入地研究 Angular 库并了解更多信息。谢谢!