简体   繁体   中英

How to filter an array of objects based of attribute from another object in Angular?

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>&nbsp;<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.

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>&nbsp;<button type="button" class="btn btn-secondary">Archive</button>
    </td>
</tr>

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