4

I have a very simple page with two selects filled in with same data (bank accounts). One is aimed to be source and another target in order to create a transaction. In this simple scenario a transaction is just an amount transfered from one account to another.

I am facing a problem while sending the transaction to my rest service. From the image bellow is easy to see that the transaction variable isn't properly filled but I don't know what is going wrong.

I see the first account (source) from transaction partially filled and the second (target) with [object Object]. Why wasn't filled the same since they are exactly same type of object and why in the first one name wasn't filled?

new-transaction.html:

<div>
  <div>
  <label>Source Account</label>
  <select [(ngModel)]="transaction.sourceAccount"> 

      <option *ngFor="let a of accounts" [value]="a" >{{a.name}}</option>
  </select>
</div>
<div>
  <label>Target Account</label>
  <select  [(ngModel)]="transaction.targetAccount" > 
      <option *ngFor="let a of accounts" [value]="a">{{a.name}}</option>
  </select>
</div>
<div>
  <input type="text" [(ngModel)]="transaction.amount">
</div>
<div>
    <button (click)="addTransaction()">Add</button>
 </div>
</div>

new-transaction.component.ts

...
export class NewTransactionComponent implements OnInit {

  accounts: Account[];
  transaction: Transaction = new Transaction();

  constructor(private accountService: AccountService, private transactionService: TransactionService) { }

  ngOnInit(): void {
    this.transaction.targetAccount = new Account();
    this.transaction.sourceAccount = new Account();

    this.accountService
    .getAllAccounts()
    .subscribe(
      (accounts) => {
        this.accounts = accounts;
      }
    );
  }

  addTransaction(): void {
    this.transactionService.addTransaction(this.transaction)    
    .subscribe(
      (transaction) => {
        this.transaction = transaction;
      }
    );
    //this.router.navigate(['/home']);
  } 
}

transaction.service.ts

...

  public addTransaction(transaction: Transaction): Observable<Transaction> {
    return this.http
      .post(API_URL + '/transaction', transaction)
      .map(response => {
        return new Transaction(response.json());
      })
      .catch(this.handleError);
  }

transaction.ts

import { Account } from "./account";

export class Transaction {
    idtransaction: number;
    amount: number;
    sourceAccount: Account;
    targetAccount: Account;

    constructor(values: Object = {}) {
      Object.assign(this, values);
    }
}

account.ts

import { User } from "./user";

export class Account {
    id: number;
    name: string = '';
    user: User[] = [];

    constructor(values: Object = {}) {
      Object.assign(this, values);
    }
}

enter image description here

*** edited

enter image description here

Jim C
  • 3,957
  • 25
  • 85
  • 162

2 Answers2

12

I think you're missing the use of the ngValue directive here to have the object reflected in the value bound to ngModel.

<label>Target Account</label>
<select  [(ngModel)]="transaction.targetAccount" > 
    <option *ngFor="let a of accounts" [ngValue]="a">{{a.name}}</option>
</select>

From the documentation .. If your option values happen to be objects (and you'd like to save the selection in your form as an object), use ngValue instead.

See here for the full docs for the select element.

Note I'm not sure why it seems to be working on the sourceAccount property.

Garth Mason
  • 7,611
  • 3
  • 30
  • 39
1

I got this working with the following code. I like what Garth posted, and it would be less code, too. I had this ready just minutes after seeing his answer, so I am going to post it since I spent some time on it.

new-transaction.component.html

<div>
  <div>
  <label>Source Account</label>
  <select [(ngModel)]="sourceBankAccount" name="sourceBankAccount" (change)="sourceAccountChanged($event.target.value)" >

      <option *ngFor="let a of accounts" [value]="a.id" >{{a.name}}</option>
  </select>
</div>
<div>
  <label>Target Account</label>
  <select  [(ngModel)]="targetBankAccount"  name="targetBankAccount" (change)="targetAccountChanged($event.target.value)" >
      <option *ngFor="let a of accounts" [value]="a.id">{{a.name}}</option>
  </select>
</div>
<div>
  <input type="text" [(ngModel)]="transaction.amount">
</div>
<div>
    <button (click)="addTransaction()">Add</button>
 </div>
</div>

Notice that this uses the account id for the value in the drop down. There are change event calls here, too.

new-transaction.component.ts

export class NewTransactionComponent implements OnInit {

  accounts: BankAccount[];
  transaction: Transaction = new Transaction();
  sourceBankAccount: number;
  targetBankAccount: number;

  constructor(private accountService: BankAccountService, private transactionService: TransactionService) { }

  ngOnInit(): void {
    this.accountService
    .getAllAccounts()
    .subscribe(
      (accounts) => {
        this.accounts = accounts;
      }
    );
  }

  sourceAccountChanged(account: number) {
    const newAccount: BankAccount = this.accounts.find(acct => acct.id === +account);
    if (newAccount) {
      this.transaction.sourceAccount = new BankAccount(newAccount);
    }
  }

  targetAccountChanged(account: number) {
    const newAccount: BankAccount = this.accounts.find(acct => acct.id === +account);
    if (newAccount) {
      this.transaction.targetAccount = new BankAccount(newAccount);
    }
  }

  addTransaction(): void {
    console.log(this.transaction);
    this.transactionService.addTransaction(this.transaction)
    .subscribe(
      (transaction) => {
        this.transaction = transaction;
      }
    );
  }
}
R. Richards
  • 24,603
  • 10
  • 64
  • 64
  • Yes, this solution would also work - I've used this with – Garth Mason Feb 05 '18 at 05:59
  • Kindly, can you see the picture just added? I am getting undefined when I choose the account – Jim C Feb 05 '18 at 10:39
  • Sorry, ignore my last comment. It was a typo – Jim C Feb 05 '18 at 11:17
  • definitely you answered my question. As a plus if you don't mind, why after I choose the account the select box doesn't show the option selected? – Jim C Feb 05 '18 at 11:24
  • I am not seeing that problem in the code that I am using. When I select something in the account dropdowns, it shows what I selected. I didn't add any styling into my project, but I doubt anything like that might affect anything. – R. Richards Feb 05 '18 at 12:59
  • I do see in your screenshot that the name of the source account is empty. Could that be why you are not seeing anything? – R. Richards Feb 05 '18 at 13:05
  • I don't see that error either, works ok for me. Also noticed that rather than using the (change) event you could instead tap hook into (ngModelChange) - so then you don't need to pass the $event.target.value parameter, just pickup the current value of the model e.g. targetBankAccount – Garth Mason Feb 06 '18 at 00:12