I managed to bind the data from this.empDetails.services correctly on the UI, checkboxes are checked correctly, and also listed all the checkboxes options.
the "easy" way is to create a FormArray with values true/false. see the example in stackblitz
Update:Correct some errors
You fill the formArray using the data and the sservicesOptions
getFormArrayService(data:any[]):FormArray
{
//e.g. data=['mopping','washingclothes']
// return a FormArray and the value will be [false,true,false,true]
//if data=null, return a FormArray [false,false,false,false]
return new FormArray(
this.sservicesOptions.map(x=>new FormControl(data?data.find(dat=>dat==x.value)?true:false:false))
)
}
So, you can, in ngInit make some like
ngOnInit()
{
this.updateSvcForm=new FormGroup({
sservices:this.getFormArrayService(null)
})
}
And in submitting the form, transform the value
submit(updateSvcForm)
{
if (updateSvcForm.valid)
{
let services:string[]=[];
updateSvcForm.value.sservices.forEach((x,index)=>
{
if (x)
services.push(this.sservicesOptions.value)
})
const result={
...updateSvcForm.value, //all value of the form but
sservices:services
}
console.log(result)
}
}
The .html becomes like
{{updateSvcForm?.value|json}}
The "not so easy way" a customFormControl, see an example in stackblitz
Basically, we create a series of the checkbox, each change in checkbox return the "booleansToProp". In the example, I add a property "required", then indicate it is invalid if no check is checked and isString if we can return a string, not an array
@Component({
selector: 'check-box-group',
template: `
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CheckBoxGroupComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => CheckBoxGroupComponent),
multi: true,
}
],
styles:[`
.focused {
outline: black dotted thin;
}`
]
})
export class CheckBoxGroupComponent implements ControlValueAccessor {
@Input()
set source(value)
{
this._source=value;
//we need to know which column has the "value" and which column has the "text"
//replace all extrange character else ":" and ","
let aux=JSON.stringify(value[0]).replace(/[^\w|:|,\s]/gi, '').split(',');
this._key=aux[0].split(':')[0]
this._col=aux[1].split(':')[0]
}
get source()
{
return this._source;
}
_selectedItems: any[] = [];
_source;
_key: string;
_col: string;
_name:string="";
_isString:boolean=false;
_isRequired:boolean=false;
onChange;
onTouched;
constructor(el:ElementRef) {
let name=el.nativeElement.getAttribute('name');
//we store in this._isRequired if the element has an attribute "required"
this._isRequired=el.nativeElement.getAttribute('isRequired')!=null?true:false;
//idem if the element has an attribute "isString"
this._isString=el.nativeElement.getAttribute('isString')!=null?true:false;
//Is necesary give a name to the control if there're severals check-box-group
this._name=name?name:"ck";
}
writeValue(value: any[]|any): void {
this._selectedItems = this._isString?
this.propsToBoolean(value?value.split(','):""):this.propsToBoolean(value);
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
}
//setValue is called each time you check/uncheck a checkbox
//Simple call to this.onChange with the value o the result of the
//function this.booleanToProps
setValue(value: boolean, index: number) {
this._selectedItems[index] = value;
this.onChange(this._isString?
this.booleanToProps(this._selectedItems).join(','):
this.booleanToProps(this._selectedItems));
}
validate(control: AbstractControl): ValidationErrors | null{
if (!this._isRequired)
return null;
if (!this._selectedItems.find(x=>x))
return {error:"you must select one option at last"}
return null
}
//we received an array (or a string separated by commas) and
//return an array of true/false
propsToBoolean(props): any[] {
let propsString=props?props.map(x=>''+x):null;
return props ? this.source.map((x: any) => propsString.indexOf(''+x[this._key]) >= 0)
: this.source.map(x => false);
}
//we received an array of true/false and return an array with the values
//or with teh values separated by commas
booleanToProps(propsBoolean: boolean[]) {
let props: any[] = [];
if (propsBoolean) {
propsBoolean.forEach((item, index) => {
if (item)
props.push(this.source[index][this._key])
})
}
return props;
}
}