Eduardo Siguenza
Eduardo Siguenza

Reputation: 11

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?

Upvotes: 1

Views: 2804

Answers (3)

thomi
thomi

Reputation: 1687

The from() operator creates an observable which fires 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')),
 );

Upvotes: 0

onik
onik

Reputation: 1631

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
     } );
  }

Upvotes: 2

Phix
Phix

Reputation: 9880

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

Upvotes: 3

Related Questions