简体   繁体   中英

Groupby Observable <Object[]> Angular 7

I'm trying to group array elements based on the value that I get back from the observable and show those groups as a different <mat-card> on the view.

I have an in-memory-web-api-module which contains a class of the simplified example below.

I'm still learning Angular so please excuse my noobness

Ex: in-memory-data.service.ts

export class InMemoryDataService implements InMemoryDbService {
createDb(){
const apps = [
{id: '1', app: 'app1', env_id:2, env:'DEV'},
{id: '2', app: 'app2' env_id:2, env:'DEV'},
{id: '4', app: 'app3' env_id:2, env:'DEV'},
{id: '5', app: 'app1' env_id:3, env:'Test'},
{id: '6', app: 'app2' env_id:3, env:'Test'},
{id: '7', app: 'app3' env_id:1, env:'PROD'},
];
return {apps}
}

For each of the different app values a new <mat-card> should be created, not for every app value which is exactly what it's doing now.

Here's my code.

applications.service.ts EDITED:

import { Injectable } from '@angular/core';
import { Apps } from './applications';
import { APPS } from './mock-applications';
import { Observable, of } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { MessagesService } from './messages.service';
import { catchError, map, switchMap, tap, groupBy, mergeMap } from 'rxjs/operators';

export class ApplicationsService {

private applicationUrl = 'api/apps';

constructor(private messageService: MessagesService, private http: HttpClient ) { }

getApps(): Observable<Apps[]> {
  return this.http.get<Apps[]>(this.applicationUrl)
    .pipe(
      groupBy(application=> application.app),
      mergeMap(obs => {
        retun obs.pipe(
          toArray(),
          map(apps => {
            return {id: obs.key, group: apps}
          })
        )
      }),
      toArray(),
      tap(_ => this.log('fetched apps')),
      catchError(this.handleError<Apps[]>('getApps', []))
    )
}

/*Type 'Observable<Apps[] | {}[]>' is not assignable to type 'Observable<Apps[]>'.
  Type 'Apps[] | {}[]' is not assignable to type 'Apps[]'.
    Type '{}[]' is not assignable to type 'Apps[]'.
      Type '{}' is missing the following properties from type 'Apps': guid, mots_spi_indicator, mots_sox_indicator, mots_pci_indicator, and 42 more./*

getApp(id: string): Observable<Apps> {
  const url = `${this.applicationUrl}/${id}`;
  return this.http.get<Apps>(url).pipe(
    tap(_ => this.log(`fetched app guid=${id}`)),
    catchError(this.handleError<Apps>(`getApp guid=${id}`))
  );

}
}

home.component.ts

import { Component, OnInit } from '@angular/core';
import { Apps } from '../../applications'
import { APPS } from '../../mock-applications';
import { ApplicationsService } from 'src/app/applications.service';

export class HomeComponent implements OnInit {

  apps: Apps[];

  constructor(private appService: ApplicationsService) { }

  ngOnInit() {
    this.getApps();
  }

  getApps(): void{
    this.appService.getApps()
    .subscribe(apps =>  this.apps = apps);
  }

}

home.component.html

<h1>My Access</h1>
<app-app-search></app-app-search>

<div fxLayout="row wrap">
  <div *ngFor="let app of apps" fxFlex.gt-sm="25" fxFlex.gt-xs="50" fxFlex="100">

    <a routerLink="/detail/{{app.id}}">
      <mat-card class="card-widget mat-teal">
        <div mat-card-widget>
          <div mat-card-float-icon>
            <mat-icon>error</mat-icon>
          </div>
          <div class="pl-0">
            <h2 mat-card-widget-title>{{app.app}}</h2>
          </div>
        </div>
      </mat-card>
    </a>

  </div>
</div> 

How can I make it so every app name is put into it's own group?

Here's one way to group:

export class AppComponent implements OnInit {
  apps$: Observable<any>;
  createDb() {
    const apps = [
      { id: '1', app: 'app1', env_id: 2, env: 'DEV' },
      { id: '2', app: 'app2', env_id: 2, env: 'DEV' },
      { id: '4', app: 'app3', env_id: 2, env: 'DEV' },
      { id: '5', app: 'app1', env_id: 3, env: 'Test' },
      { id: '6', app: 'app2', env_id: 3, env: 'Test' },
      { id: '7', app: 'app3', env_id: 1, env: 'PROD' },
    ];
    return from(apps)
  }

  ngOnInit() {
    this.apps$  = this.createDb().pipe(
      // Tell rx which property to group by
      groupBy(application => application.app),
      mergeMap(obs => {
        // groupBy returns a GroupedObservable, so we need to expand that out
        return obs.pipe(
          toArray(), 
          map(apps => {
            return {id: obs.key, group: apps}
          })
        )
      }),
      toArray(),
    )
  }
}
<div *ngFor="let app of apps$ | async">
  <h4>{{ app.id }}</h4>
  <ul>
    <li *ngFor="let item of app.group">{{ item | json }}</li>
  </ul>
</div>

Output:

app1
  { "id": "1", "app": "app1", "env_id": 2, "env": "DEV" }
  { "id": "5", "app": "app1", "env_id": 3, "env": "Test" }
app2
  { "id": "2", "app": "app2", "env_id": 2, "env": "DEV" }
  { "id": "6", "app": "app2", "env_id": 3, "env": "Test" }
app3
  { "id": "4", "app": "app3", "env_id": 2, "env": "DEV" }
  { "id": "7", "app": "app3", "env_id": 1, "env": "PROD" }

Stackblitz

use lodash for that

npm install lodash

import * as groupBy from "lodash/groupBy";

...

getApps(): void{
    this.appService.getApps()
    .subscribe(apps => {
       var grouppedApps = groupBy(apps,"app");
       // You can do whatever you want now
     } );
  }

The from() operator creates an observable which f ires the elements of the array and then terminates . The toArray() Operator collects all source emissions and emits them as an array when the source completes . So for a use case where you do not have a fixed set of values you want to emit, you cannot use toArray() . You can however use map together with _.groupBy() from lodash . Here's what I did for this particular case:

  this.filteredMeetingList$ = this.sorting$.pipe(
  switchMap(sorting => this.meetingList$.pipe(
    // ...
    map(sMeetings => sMeetings.map(m => ({
      ...m,
      day: moment(m.startDateTime).format('DD. MMM'),
    }))),
  )),
  map(elem => _.groupBy(elem, 'day')),
 );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM