1

My problem is that I have a form, and when a user submits the form, I want to receive the data which the user has submitted. This data should be wrapped in a single object. I use the formBuilder in Angular, but it seems as if it is mostly possible to retrieve data with formBuilder from <input> tags (quote from the top answer in this post: "formControlName only works on input, select and textarea. anything that has "value" property."

I have defined a form within my constructor and initialized fields, making use of the formBuilder;

component.ts

constructor(private formBuilder: FormBuilder,
) {
  this.checkoutForm = this.formBuilder.group({
    selectedAccount: '',
    selectedContactInfo: '',
  });
}

In my html, I have two places where a user should choose something from; a dropdown-menu and a selection of checkboxes. selectedAccount from my constructor is applied to <mat-select>, which works fine; on submission of form, I can see which account I chose. However, it seems that I can't apply formControlName="selectedContactInfo" to the div in checkboxes area; it returns empty string.

component.html

<!-- DROPDOWN - WORKS PERFECT -->
<mat-form-field>
  <mat-label>CHOOSE ACCOUNT</mat-label>
  <mat-select formControlName="selectedAccount">
    <mat-option *ngFor="let account of accounts" [value]="account.name">{{account.name}}</mat-option>
  </mat-select>
</mat-form-field>

<!-- CHECKBOXES - DOESN'T RETRIEVE DATA WHEN FORM IS SUBMITTED -->
h2 class="mat-h2">CHOOSE TELEPHONE NUMBERS WE CAN CONTACT YOU ON</h2>
<div formControlName="selectedContactInfo" class="item-container">
  <mat-checkbox>{{contactInfo.privateMobile}}</mat-checkbox>
  <mat-checkbox>{{contactInfo.workMobile}}</mat-checkbox>
</div>

From the checkboxes, I want to retrieve the actual data. So if the first checkbox has been checked, I want to receive whatever data that contactInfo.privateMobile contains, probably a string or int of 8 digits, and assign it to selectedContactInfo in the constructor, in the very same manner I did for selectedAccount and the dropdown menu.

I want selectedContactInfo to contain the data for the checkboxes; if both are checked, it returns the mobile numbers for both. If one is checked, it returns that number etc. I also tried with selectedContactInfo: [] in constructor since it needs to return data for all checkboxes, but that just returns null right now.

Thanks in advance.

Edit: For reference (if anyone wants to know this), this is how I check the data when I submit my form to see if it contains right info. Dont know if its relevant:

onSubmit(userData) {
  console.log(userData);
}
thesystem
  • 554
  • 1
  • 10
  • 31

3 Answers3

1

Please check this example

@Component({
  template: `
    <form [formGroup]="form">
      <input type="checkbox" formControlName="rememberLogin"> Remember login
    </form>
    <p>Form value: {{ form.value | json }}</p>  
    <!-- {rememberLogin: true } -->
  `,
})
export class App {
  constructor(public fb: FormBuilder) {
    this.form = this.fb.group({
        rememberLogin: [true]
    });
  }  
}
Wiki
  • 224
  • 6
  • 21
  • Thanks for the comment. The `console.log` just returns a boolean of whether the box has been clicked in real-time. I want the value, "Check me!", to be submitted together with other elements of the form in a single object as a part of the formBuilder.group - that is, assign it to `selectedContactInfo` or other variables initialized there. Using this approach, I could just as well disband the formBuilder. – thesystem Oct 04 '19 at 13:37
  • 1
    you want to use form builder to get value in checkbox? – Wiki Oct 04 '19 at 13:39
  • Yes, in the same manner that I used the form builder to get the value for the dropdown menu. I edited my original post of what I want to receive from checkboxes (right under the code of component.html) – thesystem Oct 04 '19 at 13:40
  • 1
    please check [link](https://stackoverflow.com/questions/40927167/angular-reactiveforms-producing-an-array-of-checkbox-values) similar answer and also [link](https://stackoverflow.com/questions/48000307/angular-5-reactive-form-set-mat-checkbox-to-check) – Wiki Oct 04 '19 at 13:46
  • 1
    i have also updated my answer with input type="checkbox" if the given link not helpful then try given example – Wiki Oct 04 '19 at 13:52
  • thank you very much for the help! i was AFK, will check out all the links now and return with answer when I tried them out – thesystem Oct 04 '19 at 14:16
  • Seems its impossible to extract the value from a ``. Thanks for your effort! – thesystem Oct 04 '19 at 15:04
1

The problem

The output you want is an array of numbers while the selection will return true or false since it is a checkbox. In situations like this where the form contains different values from what you desire, you can use valueChanges to detect the changes in a particular FormControl and extract the desired result from the FormControl or FormArray.

Solution

Since you want an array, the first thing you need to do is create a FormArray for selectedContactInfo.

contactInfo = {
    privateMobile: 1800124152,
    workMobile: 1800124124
};

this.checkoutForm = this.formBuilder.group({
    selectedAccount: '',
    selectedContactInfo: this.formBuilder.array(Object.keys(this.contactInfo).map(key => false))
});

Note that we are initializing the FormArray using contactInfo. This will basically get the keys from the object and create an array of the same length with false values (since initially the checkbox values are false). Of course, if you need it to be checked by default, you will need to map the selection to the checkboxes but for the sake of this example, we will start with all false values.

Registering the FormArray in the template

Set the FormArrayName in the template so we access the true/false values from the checkbox. Use the KeyValuePipe since we are iterating over contactInfo which is an object.

<div class="item-container" formArrayName="selectedContactInfo">
    <ng-container *ngFor="let contactNumber of contactInfo | keyvalue; let i = index">
        <mat-checkbox [formControlName]="i">{{contactNumber.value}}</mat-checkbox>
    </ng-container>
</div>

Map the FormArray in valueChanges

Now, selectContactInfo will be an array of true/false values according to the selection of the checkboxes. This is not what we desire though, so we will subscribe to valueChanges and map the data to contactInfo and store it in a variable called selectedContactInfo (Note that this is different from the FormArray).

const control = this.checkoutForm.controls.selectedContactInfo;
this.subscription = control.valueChanges.subscribe(value => {
    this.selectedContactInfo = Object.keys(this.contactInfo).map((contactNo, index) => 
        control.value[index] ? this.contactInfo[contactNo] : null
    )
    .filter(contactNo => !!contactNo);
});

What we are doing here is basically getting the true/false array from the FormArray and then mapping it to contactInfo. If the checkbox value is true, return the number, else return null. Then use a filter to filter out the null values.

Don't forget to unsubscribe from the valueChanges subscription.

ngOnDestroy() {
    this.subscription.unsubscribe();
}

StackBlitz example

nash11
  • 8,220
  • 3
  • 19
  • 55
  • Hey nash, thanks a lot for the comprehensive answer, I appreciate it a lot! I will report back when Ive tried this tomorrow! However, I hope this works for me too, since you instantiate contactInfo with numbers, while I populate the checkbox values from a REST service. I will accept your answer if it works! – thesystem Oct 06 '19 at 15:13
  • Anytime :) The REST service shouldn't make a difference if it's the same as the sample data I've used. If you need more clarity I just answered a similar [question](https://stackoverflow.com/questions/40927167/angular-reactiveforms-producing-an-array-of-checkbox-values) after seeing the link provided by @WaqarAli in the comments in his answer. I didn't find a satisfactory answer to that question so I provided my own [answer](https://stackoverflow.com/a/58261847/9739129) there. I've also used an array of objects there as it seems like a better structure and is easier to work with than objects. – nash11 Oct 06 '19 at 23:19
  • I have also directly modified the `FormArray` in that example unlike here where I used a separate variable. – nash11 Oct 06 '19 at 23:29
0

It doesn't returns data, because you need to add formControlName for the checkbox itself, you can't add it to divs. Just add two contact infos and then you use those that have values, or you can just remove the empty one from the object.

Natixco
  • 651
  • 5
  • 20
  • Thanks for the comment, I have already tried that. That doesn't work, because It only returns a boolean value of whether the checkbox has been checked or not. – thesystem Oct 04 '19 at 13:32