Reputation: 16726
I have the following component:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-balance',
templateUrl: './balance.component.html',
styleUrls: ['./balance.component.scss']
})
export class BalanceComponent implements OnInit {
advertisers: [
{'advertiser_id': 1, 'name': 'foo'},
{'advertiser_id': 2, 'name': 'bar'},
{'advertiser_id': 3, 'name': 'cat'},
];
balances: [
{'advertiser_id': 1, 'value': '100'},
{'advertiser_id': 2, 'value': '200'},
{'advertiser_id': 3, 'value': '300'},
{'advertiser_id': 3, 'value': '500'},
];
constructor() {}
}
On my template, i'm looping through the balances to display them in a table. I'd like to display the advertiser's name along side the balance row. Therefore, i've tried the following:
<div class="card mb-3 shadow-sm">
<table class="table mb-0">
<tr>
<th>Advertiser</th>
<th class="text-right">Outstanding Balance</th>
<th class="text-center">Invoices</th>
</tr>
<tr *ngFor="let balance of balances">
<td class="align-middle">
<span ng-repeat="advertiser in advertisers | filter : { advertiser_id : balance.advertiser_id }">{{ advertiser.name}}</span>
</td>
<td class="align-middle text-right">{{ balance.value }}</td>
<td class="text-center">
<button type="button" class="btn btn-success">New</button> <button type="button" class="btn btn-secondary">Archive</button>
</td>
</tr>
</table>
</div>
However, when doing so I get the following error:
ERROR TypeError: Cannot read property 'name' of undefined
I believe the 'filter' pipe should do the trick, but not sure why it's not working.
Upvotes: 1
Views: 949
Reputation: 1645
Create a pipe.
@Pipe({
name: 'myFilterPipe'
})
export class MyFilterPipe implements PipeTransform {
transform(advertisers: any[], advertiser_id: any): any[] {
return (advertisers || []).filter(advertiser => advertiser.advertiser_id===advertiser_id)
}
}
and then replace
<span ng-repeat="advertiser in advertisers | filter : { advertiser_id : balance.advertiser_id }">{{ advertiser.name}}</span>
with
<span *ngFor="let advertiser of advertisers | myFilterPipe: balance.advertiser_id">{{ advertiser.name}}</span>
Or you can transform data before rendering (much better)
private balances: BehaviorSubject<any[]> = new BehaviorSubject([]);
private advertisers: BehaviorSubject<any[]> = new BehaviorSubject([]);
data$: Observable<any[]> = combineLatest(this.balances.asObservable(), this.advertisers.asObservable()).pipe(
map([balances,advertisers] => {
return balances.map(balance => {
balance.advertisers = advertisers.filter(advertiser=>advertiser.advertiser_id===balance.advertiser_id);
return balance;
})
})
)
and then
<tr *ngFor="let balance of data$ | async">
<td class="align-middle">
<span *ngFor="let advertiser of balance.advertisers">{{ advertiser.name}}</span>
</td>
<td class="align-middle text-right">{{ balance.value }}</td>
<td class="text-center">
<button type="button" class="btn btn-success">New</button> <button type="button" class="btn btn-secondary">Archive</button>
</td>
</tr>
Upvotes: 1