2

I am building an app Using Angular 5 and I am trying to build a dynamic form. This form, which is like a bet slip or shopping cart, is a array of objects which comes from a service as an observable. I am having difficulty figuring out how to add an input field and bind it to a property in each bet object.

The observable betSlipItems array looks like this:

[
  {
    "Id": 1,
    "betslipTeamName": "La Rochelle",
    "stake": null
  },
  {
    "Id": 2,
    "betslipTeamName": "North Queensland Cowboys",
    "stake": null
  }
] 

And what I want to do is create an input field and bind it to the 'stake' property of each bet object.

This is my code for bet-slip.component.ts so far:

import { FormBuilder, FormGroup, FormArray } from '@angular/forms';
import { Component, OnInit, Input } from '@angular/core';
import { Bets } from '../../../shared/models';
import { BetService } from '../../../shared/services/bet.service';
import { Observable } from 'rxjs';
import { of } from 'rxjs/observable/of';

@Component({
  selector: 'bet-slip',
  templateUrl: './bet-slip.component.html',
  styleUrls: ['./bet-slip.component.scss']
})

export class BetSlipComponent implements OnInit {

  public betSlipItems$: Observable<Bets[]> = of([]);
  public betSlipItems: Bets[] = [];
  public betsForm: FormGroup;

  constructor( private betService: BetService, private _fb: FormBuilder) {
    this.betSlipItems$ = this.betService.getBets();
  }

  ngOnInit() {

    this.betsForm = new FormGroup({
        stake: new FormControl()
    });

    this.betSlipItems$.subscribe((betSlipItems) => {
      this.betSlipItems = betSlipItems;
    });

  }
}

and my component bet-slip.component.html looks like this:

 <form [formGroup]="betsForm">
      <div *ngFor="let bet of betSlipItems; let i=index">
        {{bet.betslipTeamName }}
        <input type="text" formControlName="stake">
      </div>
 </form>

I know this is incorrect. How can I fix this?

3
  • use a formArray see angular.io/guide/… Commented Jan 7, 2018 at 9:49
  • here the formControlName is / should be dynamic right like a form arry or something ? Commented Jan 7, 2018 at 9:59
  • @Eliseo - thanks. I did try that but couldn't get it to work properly with the observable as I don't have very much experience with them. Commented Jan 7, 2018 at 10:08

1 Answer 1

1

The problem is that you must know what need and what expect. I suppose you want to make a form Array with "Id","betslipTeamName" and "stake" (as minimum you need Id and stake) not only stake. To work with a ReactiveForm with an array it's easy. In general we have a service that return our data. I put a full example, I expect this help you

//simple service that read a json (you have one yet -it's only to complete my example-)
@Injectable()
export class AppDataService {

  constructor(private httpClient:HttpClient) { }
  read(key:any)
  {
    return this.httpClient.get('../assets/data.json')
  }
}

Our component have a .html like this

<div *ngIf="yet">
<!--I put a *ngIf to avoid an error at first-->
  <form [formGroup]="dataForm" (ngSubmit)="submit(dataForm)" novalidate>
    <!--see that the "formArrayName" is "lista" 
    don't confuse with the *ngFor of "lista" (the lista in ngFor is a getter)
    -->
    <div formArrayName="lista" *ngFor="let lista of lista.controls;let i=index">
      <div [formGroupName]="i"> <!--it's necesary a formGroupName=i-->
      <!--we can use labels here -->
      {{labels[i].Id}}{{labels[i].Text}}
          <!--the input we need , some can be readOnly -->

          <input type="text" class="form-control" formControlName="Id">
          <input type="text" class="form-control" formControlName="betslipTeamName">
          <input type="text" class="form-control" formControlName="stake">
      </div>
      <hr>
    </div>
  </form>
   <!---this it's only to check the value of the form-->
  {{dataForm?.value |json}}
</div>

The component.ts it's like

@Component({
  selector: 'app-app-form',
  templateUrl: './app-form.component.html',
  styleUrls: ['./app-form.component.css']
})
export class AppFormComponent implements OnInit {

  dataForm: null | undefined | FormGroup;  //we have a dataForm
  data: any;  //normally we have a data too. It's not necesary that the data was identical to dataFrom 
  labels:any[] //we create an array [{Id,Text}]
  yet:boolean=false;  //I use this variable when all it's ready

  get lista() {  //<--This is the gettet that we use in the *ngFor
    return (this.dataForm) ? this.dataForm.get('lista') : null;
  };

  constructor(private fb: FormBuilder, private dataService: AppDataService) { }

  ngOnInit() {
    let key: any;
    this.dataService.read(key).subscribe(
      (response:any[]) => {
    if (response) {
      this.data = response; //this.data is an array[{"Id": 1,..}.{"Id":2..}]
      //You can create a label array with Id,Text
      this.labels=response.map((item:any)=>{return {Id:item.Id,Text:item.betslipTeamName}})
      this.dataForm = this.createFormGroup(this.data);// dataForms is an object {lista:[{..},{..}]}
      this.yet=true;
    }
  });
  }
  submit(dataForm:any)
  {
    if (dataForm.valid)
    {
      console.log(dataForm.value);
    }
  }
  createFormGroup(data: any) {
    let lista = this.buildArray(data);  //create an array of Controls

    let dataForm = this.fb.group({
      lista: lista
    });
    //see that dataForms is an object {lista:[{..},{..}]}
    return dataForm;
  }

  buildArray(myArray: any[]) {
    //witch each data, we create a fbGroup 
    const arr = myArray.map(data => {
      return this.fb.group({
        "Id": [data.Id], //we can omit some control
        "betslipTeamName": [data.betslipTeamName],
        "stake": [data.stake],
      });
    });
    //And return a array of fbGroup
    return this.fb.array(arr);
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Many thanks for your answer. I just added this code and it worked! Thank you!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.