0

The form is having 1 textbox , 1 radio button and 1 multi select Checkbox The HTML template is like below

        <form  *ngIf="notificationSettings | async; else loading"
              [formGroup]="notificationForm" (ngSubmit)="onSubmit()">
              
          <div class="form-group">        
            <div *ngFor="let option of notifyBackAlertOptions; let i=index">
              <input type="checkbox" class="form-check-input" [value]="option.value" formControlName="notifyBackOptions"  />
              <label> {{option.name}} </label>
            </div>
          </div>
          <div class="form-group">
            <label for="notifyBackEmail">Where shall we send the alerts?</label>
            <input type="email" class="form-control" formControlName="notifyBackEmail">
          </div>
          
          <div class="form-check" *ngFor="let option of discontinuedAlertOptions;">
            <label>
              <input formControlName="discontinuedOption" class="form-check-input"
                     type="radio"
                     name="discontinuedOption"
                     [value]="option.value" />
              {{option.name}}
            </label>
          </div>

          <div class="float-left">
            <button class="btn btn-primary mr-1">Update</button>        
          </div>

        </form>

        <ng-template #loading>
          Loading ---...
        </ng-template>
        

The component is like below

    import { Observable } from 'rxjs';
    import { tap } from 'rxjs/operators';


    export class NotifcationsComponent implements OnInit {
      
      notificationSettings: Observable<NotificationSetting>;
      notificationForm: FormGroup;
      submitted = false; 
      
      notifyBackAlertOptions = [
        { name: 'Option 1', value: '1' },
        { name: 'Option 2', value: '2' },
        { name: 'Option 3', value: '3' }, 
        { name: 'Option 4', value: '4' }    
      ];
      discontinuedAlertOptions = [
        { name: 'Yes for any', value: '1' },   
        {name: 'Yes for all', value: '2' },
        { name: 'No', value: '3' }
      ];   

      constructor(private formBuilder: FormBuilder,private userService: UserService)  { }

      ngOnInit() {

        this.getCurrentSettings(); 
        this.notificationForm = this.formBuilder.group({
          notifyBackEmail: [''], 
          discontinuedOption: [''],   
          notifyBackOptions: new FormArray([]),
        });
        
        
      }

      getCurrentSettings(): void {



        this.notificationSettings =   this.userService
          .getUserNotificationSettings()
          .pipe(tap(data => {
            console.log("GET")
         
            this.notificationForm = this.formBuilder.group({
              notifyBackEmail: new FormControl(data.notifyBackEmail),                 
              discontinuedOption: new FormControl(data.discontinuedOption),
              notifyBackOptions: new FormControl(data.notifyBackOptions)
            });

            console.log(this.notificationForm) //I can see the values are mapping correctly against notificationForm. Checkbox  property ie notifyBackOptions value is coming as ["1", "2"] at this stage 

            }        
           ));
          
          //This code maps / sets the values of textbox and radio buttons correctly at page loading based on response from API. But not setting values for multiselect checkbox correctly. 
            //I can see all checkbox values are coming as selected.          IN this case 4 checkbox values are selected      
         //How to set only  the notifyBackOptions checkbox selected values marked as checked 
      }

      // convenience getter for easy access to form fields in HTML page 
      get f() { return this.notificationForm.controls; }
     

 

      onSubmit() {
        this.submitted = true; 
        // stop here if form is invalid
        if (this.notificationForm.invalid) {
          return;
        }
        console.log(this.notificationForm.value);        
      }

       

    }
    

The HTML is rendering correctly and capturing the values at form submission . At load time of the component i have to read the values from API endpoint and prefill form based on current settings

The JSON response from API endpoint is like below

    {
    notifyBackEmail: "email@email-domain.in"
    notifyBackOptions: ["1","2"]
    discontinuedOption: "1"
    }

The existing implementation of getCurrentSettings() is setting values of radio and textbox correctly but not checkbox. At present this is setting all values of checkbox as selected. How can i set the Checkbox values as selected based on response form API with help of model binding

Sebastian
  • 4,625
  • 17
  • 76
  • 145
  • I think you need to set the checked attr of your checkboxes like [attr.checked]="option.value". - https://developer.mozilla.org/de/docs/Web/HTML/Element/Input/checkbox But i dont really get what you want to achive? There is no real condition wether the checkbox should be checked or not? – thecOdemOnkey Jun 21 '21 at 15:44
  • *ngFor will build the list of checkboxes and out of that, the items based on "notifyBackOptions" need to be marked as checked. As this is a reactive form setting FormControlName is working with all other controls like radio button , textbox etc. Not with Checkbox – Sebastian Jun 21 '21 at 15:50
  • ahh now i see sorry. okay but shouldnt the formcontrolname not be like "Option 1", "Option 2"... and so on? You could also try to patch the values on the initial form instead of overriding it with a new formgroup – thecOdemOnkey Jun 21 '21 at 16:16

1 Answers1

1

Since you are handling with array of values, you need to create array of formControl instead of single formControl. Try something like this:

Try this:

  notificationForm: FormGroup;
   
  getCurrentSettings(): void {
    this.notificationSettings = this.userService
      .getUserNotificationSettings()
      .pipe(
        tap(data => {
          const notifyBackOptions = data.notifyBackOptions;
          const notificationControls = this.notifyBackAlertOptions.map(
            item => new FormControl(notifyBackOptions.includes(item.value))
          );

          this.notificationForm = this.formBuilder.group({
            notifyBackEmail: [''],
            discontinuedOption: [''],
            notifyBackOptions: this.formBuilder.array(notificationControls)
          });
        })
      );
  }

Then in HTML You need to add formArrayName directive to sync with FormGroup

<form [formGroup]="notificationForm" (ngSubmit)="onSubmit()">
  <div class="form-group" formArrayName="notifyBackOptions">
    <div *ngFor="let option of notifyBackOptionsArr.controls; let i=index">
      <input [formControlName]="i" type="checkbox" class="form-check-input" />
      <label> {{notifyBackAlertOptions[i].name}} </label>
    </div>
  </div>
  <div class="form-group">
    <label for="notifyBackEmail">Where shall we send the alerts?</label>
    <input
      type="email"
      class="form-control"
      formControlName="notifyBackEmail"
    />
  </div>

  <div class="form-check" *ngFor="let option of discontinuedAlertOptions;">
    <label>
      <input
        formControlName="discontinuedOption"
        class="form-check-input"
        type="radio"
        name="discontinuedOption"
        [value]="option.value"
      />
      {{option.name}}
    </label>
  </div>

  <div class="float-left">
    <button class="btn btn-primary mr-1">Update</button>
  </div>
</form>

Working Example

Chellappan வ
  • 23,645
  • 3
  • 29
  • 60
  • Found one more way to handle this based on threads https://stackoverflow.com/questions/40927167/angular-reactiveforms-producing-an-array-of-checkbox-values AND https://stackblitz.com/edit/angular-validate-at-least-one-checkbox-was-selected – Sebastian Jun 22 '21 at 07:45
  • Thanks for sharing, I like the second approach.https://trungk18.com/experience/angular-form-array-validate-at-least-one-checkbox-was-selected/ – Chellappan வ Jun 22 '21 at 08:29