Reputation: 11
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
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
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
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" }
Upvotes: 3