Reputation: 460
I'm developing an Angular component which is supposed to serve as a visual list of objects. I'm trying to implement the search & filtering example from ng-bootstrap. The problem I'm having is that I cannot synchronize between fetching the data and displaying the data.
I've tried migrating from using this.antraege
to using this.filteredAntraege$
which is an Observable
.
If I don't use the Promise
between readAntraege()
and its invocation in the constructor()
, I end up having this.antraege
being empty, so with that construct I'm trying to execute search()
AFTER readAntraege()
and NOT earlier.
In the template, I could just loop over this.antraege
, but that would render me being not able to use the search mechanism (tried that before).
What's the missing brick to using the Observable
and making this work?
Thank you in advance!
Michael
the component:
import { AlertService } from './../../_alert/alert.service';
import { Component, OnInit, PipeTransform } from '@angular/core';
import { AntragService } from '../../services/antrag.service';
import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { NgbdModalConfirmAutofocus } from '../modal-focus/modal-focus.component';
import { Router } from '@angular/router';
import { Antrag } from '../../shared/antrag.model';
import { DecimalPipe } from '@angular/common';
import { Observable } from 'rxjs';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { startWith, map, filter } from 'rxjs/operators';
@Component({
selector: 'app-antrag-list',
templateUrl: './antrag-list.component.html',
styleUrls: ['./antrag-list.component.css'],
providers: [DecimalPipe]
})
export class AntragListComponent implements OnInit {
activeModal: NgbActiveModal;
antraege: Antrag[];
currentAntrag = null;
currentIndex = -1;
name = '';
filteredAntraege$: Observable<Antrag[]>;
filterfcvar: FormControl;
filterfg: FormGroup;
constructor(private antragService: AntragService,
public alertService: AlertService,
public modal: NgbActiveModal,
private router: Router,
private modalService: NgbModal,
pipe: DecimalPipe,
private fb: FormBuilder) {
this.filterfcvar = new FormControl();
this.filterfg = this.fb.group({filterfc: this.filterfcvar});
this.readAntraege().then((result) => {
// Aufbau des Datenbestandes für das Suchefeld:
this.filteredAntraege$ = this.filterfcvar.valueChanges.pipe(
startWith(''),
map(text => this.search(text, pipe))
);
}).catch((err) => {
});
}
ngOnInit(): void {
}
readAntraege(): any {
return new Promise((resolve, reject) => {
this.antragService.readAll()
.subscribe(
antraege => {
this.antraege = antraege;
console.log('readAntraege(): antraege===' + antraege);
console.log('readAntraege(): this.antraege===' + this.antraege);
},
error => {
console.log(error);
});
});
}
refresh(): void {
this.readAntraege();
this.currentAntrag = null;
this.currentIndex = -1;
}
setCurrentAntrag(antrag, index): void {
this.currentAntrag = antrag;
this.currentIndex = index;
}
search(text: string, pipe: PipeTransform): Antrag[] {
console.log('search: text===' + text);
console.log('search: pipe===' + pipe);
console.log('antrag filter beispiel: ' + this.antraege);
return this.antraege.filter(antrag => {
const term = text.toLowerCase();
return antrag.antragsteller.toLowerCase().includes(term)
|| pipe.transform(antrag.gestelltbei).includes(term)
|| pipe.transform(antrag.status).includes(term);
});
}
}
the template:
<div class="form-group">
<form [formGroup]="filterfg" novalidate>
<div class="form-group form-inline">
Volltextsuche: <input class="form-control ml-2" type="text" formControlName="filterfc"/>
</div>
</form>
</div>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">id</th>
<th scope="col">antragsteller</th>
<th scope="col">gestellt bei</th>
<th scope="col">Eingangsdatum</th>
<th scope="col">Förderhöhe</th>
<th scope="col">Status</th>
<th scope="col">Aktion</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let antrag of filteredAntraege$ | async; index as i" [class.active]="i == currentIndex"
(click)="setCurrentAntrag(antrag, i)">
<th scope="row">{{antrag.id}}</th>
<td><ngb-highlight [result]="antrag.antragsteller" [term]="filterfcvar.value"></ngb-highlight></td>
<td>{{antrag.gestelltbei}}</td>
<td>{{antrag.eingangsdatum}}</td>
<td>{{antrag.foerderhoehe | currency:'EUR':true}}</td>
<td>{{antrag.status}}</td>
<td></td>
</tr>
</tbody>
</table>
<div class="list row">
<div class="col-md-6">
<div *ngIf="currentAntrag">
<h4>Antrag</h4>
<div>
<label><strong>Nr.</strong></label>
<pre>{{currentAntrag.id}}</pre>
</div>
<div>
<label><strong>Name:</strong></label>
<pre>{{currentAntrag.antragsteller}}</pre>
</div>
<div>
<label><strong>Eingangsdatum:</strong></label>
<pre>{{currentAntrag.eingangsdatum}}</pre>
</div>
<div>
<label><strong>Förderhöhe:</strong></label>
<pre>{{currentAntrag.foerderhoehe}} {{currentAntrag.foerderhoehewaehrung}}</pre>
</div>
<div>
<label><strong>Gestellt bei:</strong></label>
<pre>{{currentAntrag.gestelltbei}}</pre>
</div>
<div>
<label><strong>Status:</strong></label>
<pre>{{currentAntrag.status}}</pre>
</div>
<a class="btn btn-warning" routerLink="/antrag/{{currentAntrag.id}}">
Bearbeiten
</a>
</div>
<div *ngIf="!currentAntrag">
<br />
<p>Bitte auf einen Antrag klicken, um Details zu sehen</p>
</div>
</div>
</div>
Upvotes: 0
Views: 823
Reputation: 1168
The problem here as you correctly stated is that you are trying to search on an empty stream of data. You can fix that by waiting for the call to get Antraege to finish and the continue with the search observable. Based on you example I would do the following
@Component({
selector: 'app-antrag-list',
templateUrl: './antrag-list.component.html',
styleUrls: ['./antrag-list.component.css'],
providers: [DecimalPipe]
})
export class AntragListComponent implements OnInit {
activeModal: NgbActiveModal;
antraege$: Observable<Antrag[]> = this.antragService.readAll()
.pipe(tap((antraege) => this.antraege = antraege));
currentAntrag = null;
currentIndex = -1;
name = '';
filteredAntraege$: Observable<Antrag[]>;
filterfcvar = new FormControl();
filterfg = this.fb.group({filterfc: this.filterfcvar});;
constructor(
private antragService: AntragService,
public alertService: AlertService,
public modal: NgbActiveModal,
private router: Router,
private modalService: NgbModal,
pipe: DecimalPipe,
private fb: FormBuilder
) {}
ngOnInit(): void {
this.filteredAntraege$ = this.filterfcvar.valueChanges.pipe(
startWith(''),
map(text => this.search(text, pipe))
);
}
setCurrentAntrag(antrag, index): void {
this.currentAntrag = antrag;
this.currentIndex = index;
}
search(text: string, pipe: PipeTransform): Antrag[] {
console.log('search: text===' + text);
console.log('search: pipe===' + pipe);
console.log('antrag filter beispiel: ' + this.antraege);
return this.antraege.filter(antrag => {
const term = text.toLowerCase();
return antrag.antragsteller.toLowerCase().includes(term)
|| antrag.gestelltbei.toLowerCase().includes(term)
|| antrag.status.status.toLowerCase().includes(term);
});
}
}
And inside the html template I would use an <ng-container>
to wait for the data to load first. Check below:
<table class="table table-striped">
<thead>
<tr>
<th scope="col">id</th>
<th scope="col">antragsteller</th>
<th scope="col">gestellt bei</th>
<th scope="col">Eingangsdatum</th>
<th scope="col">Förderhöhe</th>
<th scope="col">Status</th>
<th scope="col">Aktion</th>
</tr>
</thead>
<tbody>
<ng-container *ngIf="antraege$ | async">
<tr *ngFor="let antrag of filteredAntraege$ | async; index as i" [class.active]="i == currentIndex"
(click)="setCurrentAntrag(antrag, i)">
<th scope="row">{{antrag.id}}</th>
<td><ngb-highlight [result]="antrag.antragsteller" [term]="filterfcvar.value"></ngb-highlight></td>
<td>{{antrag.gestelltbei}}</td>
<td>{{antrag.eingangsdatum}}</td>
<td>{{antrag.foerderhoehe | currency:'EUR':true}}</td>
<td>{{antrag.status}}</td>
<td></td>
</tr>
</ng-container>
</tbody>
</table>
You can find a working example here bootstrap table filtering with async data
Upvotes: 1