Villhellm
Villhellm

Reputation: 69

Angular How to display a single object from an array in html

I am trying to display a single object from an array based on a property value.

My list of transactionitems has an accountId property, but I would like to display the account name instead. All accounts are loaded in the accounts$ array. I just can't figure out how to properly use my getAccountById function

Here is the component class

export class NewtransactionComponent implements OnInit {
  transaction$: Transaction;
  tempItem$: TransactionItem;
  accounts$: Array<Account>;

  constructor(private data: DataService) { }

  ngOnInit() {
    this.transaction$ = new Transaction();
    this.data.getAccounts().subscribe(data => this.accounts$ = Object.assign(new Array<Account>(), data));
    this.tempItem$ = new TransactionItem();
    this.transaction$.TransactionDate = new Date();
  }

  addItem(){
    this.transaction$.TransactionItems.push(this.tempItem$);
    this.tempItem$ = new TransactionItem();
  }

  getAccountById(id):Account{
    return this.accounts$.find(x => x.id === id);
  };

and here is the html view that gives the error "Cannot read property 'name' of undefined"

     <div class="items-container">
        <mat-form-field>
          <input matInput placeholder="Amount" [(ngModel)]="tempItem$.Amount">
        </mat-form-field>
        <mat-form-field *ngIf="accounts$">
          <mat-select placeholder="Account" [(ngModel)]="tempItem$.AccountId">
            <mat-option *ngFor="let account of accounts$" [value]="account.id">{{account.name}}</mat-option>
          </mat-select>
        </mat-form-field>
        <mat-form-field>
          <mat-select placeholder="Credit/Debit" [(ngModel)]="tempItem$.CreditDebit">
            <mat-option value="Credit">Credit</mat-option>
            <mat-option value="Debit">Debit</mat-option>
          </mat-select>
        </mat-form-field>
        <button mat-mini-fab color="primary" (click)="addItem()">Add</button>
      </div>
      <table *ngIf="transaction$.TransactionItems.length">
        <tr>
          <th>Amount</th>
          <th>Account</th>
          <th>Credit/Debit</th>
        </tr>
        <tr *ngFor="let item of transaction$.TransactionItems">
          <th>{{item.Amount | currency}}</th>
          <th>{{getAccountById(item.AccoundId).name}}</th>
          <th>{{item.CreditDebit}}</th>
        </tr>
      </table>

these are the account and transactionitem data models for reference

export class Account {
    Id: string;
    Name: string;
    Category: string;
    SubCategory: string;
}

export class TransactionItem{
    Id: number;
    TransactionId:number;
    Accountid: string;
    Amount: number;
    CreditDebit: string;
}

Upvotes: 2

Views: 7487

Answers (1)

DeborahK
DeborahK

Reputation: 60626

I assume that the error is here: {{account.name}}?

This is most likely a timing issue. The page will attempt to display before the data is retrieved from the subscription.

One way to resolve the issue is to use an *ngIf

 <div class="items-container" *ngIf="account">

That way the page won't try to display until the data is retrieved.

Another option is to use the safe navigation operator:

{{account?.name}}

The question mark tells Angular not to attempt to read the name property if the account is null or undefined.

EDIT:

If there error is here: {{getAccountById(item.AccountId).name}}, then it is telling you that getAccountById(item.AccountId) is undefined or null. Is it possible that one of your transactions has no account?

And looking at your code more closely, JavaScript/TypeScript is case sensitive. So if you declare the id using:

Id: string;

(Upper case I)

You can't then access it using:

return this.accounts$.find(x => x.id === id);

(Lower case)

Upvotes: 5

Related Questions