1

what is the proper way to setup this code. I want to user select checkbox and value of that checkbox to be pushed into FormArray now I get [true,false,false] output

data = ['Marka', 'Kilometraza', 'Alu felne'];

addCheckboxes() {
    this.data.map((o, i) => {
      const control = new FormControl(i === 0);
      this.checkboxes.controls.push(control);
    });
  }

HTML

<div formArrayName="checkboxes" *ngFor="let check of regForm.get('detaljne').get('checkboxes').controls;index as i">
   <mat-checkbox [formControlName]="i">{{ data[i] }}</mat-checkbox>
</div>

Now I get [true,false,false] output when first checkbox is checked but I want to get value of selected checkboxes, so only values of selected checkboxes to be in that FormArray, So when checkbox with label 'Marka' is checked than FormArray to be ['Marka']

1

2 Answers 2

3

Another way is used a custom form control. Take a look the stackblitz

The custom control admit two type of "data", a simple array like

data = ['Marka', 'Kilometraza', 'Alu felne'];

Or a array of object. the first field becomes the "key" and the second one the "text" you see

dataValue=[{value:1,text:'Marka'}, 
           {value:2,text:'Kilometraza'},
           {value:3,text:'Alu felne'}]

The code, I'll try explain n comments is like:

@Component({
  selector: 'check-box-list',
  template: `
  <div [formGroup]="formArray">
    <!--see the "special way" we refereed to the elements of formArray
         using [fromControl]="variable of loop"
    -->
    <div *ngFor="let check of formArray.controls;let i=index">
           <!--we show or _data[i] or _data[i][text], see below -->
      <mat-checkbox [formControl]="check">{{key?_data[i][text]:_data[i]}}</mat-checkbox>
    </div>
  `,
   <!--it's necesary a provider NG_VALUE_ACCESSOR-->
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CheckBoxListComponent),
      multi: true
    }
  ]
})
export class CheckBoxListComponent implements ControlValueAccessor {
  formArray: FormArray = new FormArray([])
  isValue: boolean = false;
  text: string;  //I stored in "text" the field that it's showed
  key: string;   //I stored in "key" the field that has the "value"
  _data

  @Input() set data(value) {
    this._data = value;
    //data[0] can be an array or a string/number
    //if is an object we get the "keys", e.g.
    //data[0]={value:....,text:....}, this.getOrderedKeys return an array ["value","text"]
    const keys = typeof (value[0]) == 'object' ? this.getOrderedKeys(value[0]) : null
    if (keys) {
      this.key = keys[0]
      this.text = keys[1]
    }
  }

  onChange;
  onTouched;

  writeValue(value: any[] | any): void {
    //with each value of "data" we create a formControl with value true or false
    this.formArray = new FormArray(
      this._data.map((x, index) =>
        new FormControl(value.indexOf(this.key ? this._data[index][this.key] :
                                      this._data[index]) >= 0)))

    //we subscribe to the formArray valueChanges
    this.formArray.valueChanges.subscribe((res) => {
      this.onTouched()  //we mark as touched the custom form control
      //we make a filter of data
      const response = res.indexOf(true) >= 0 ?
        this._data.filter((x, index) => this.formArray.value[index]) :
        null
      //if has key, return only the array with the keys
      this.onChange(response == null ? null : this.key ? 
                 response.map(x => x[this.key]) : 
                 response)

    })
  }


  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    //if we disabled/enabled the control simply disabled/enabled the controls
    this.formArray.controls.forEach(c => {
      if (isDisabled)
        c.disable();
      else
        c.enable();
    })
  }

  getOrderedKeys(obj): string[] {
    return JSON.stringify(obj)
      .replace(/[&\/\\#+()$~%.'"*?<>{}]/g, '')
      .split(',')
      .map(x => x.split(':')[0]);
  }
}

We can use like

<form [formGroup]="form3">
  <check-box-list [data]="dataValue" formControlName="checkboxes">
  </check-box-list>
 </form>
//where
   this.form3=new FormGroup(
      {
        checkboxes:new FormControl([1,3])
      }
    )
Sign up to request clarification or add additional context in comments.

1 Comment

Could you please answer this stackoverflow.com/q/64888951/13596406
0

You can merge value received from formArray with data:

newCheckboxValue.map((value, index) => value ? data[index] : null).filter(value => value)

Transforming bool array to array of labels/null, then removing null

3 Comments

to put in submit() function? @Andrey Valikov
if you need this array of checked values only when submit, then yes use this.checkboxes.getRawValue().map((value, index)... and so on instead of newCheckboxValue
why not use filter? const values=this.form.get('checkboxes').value;return this.data.filter((x,index)=>values[index]), see stackblitz.com/edit/…

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.