admin管理员组文章数量:1302498
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.
However, the data are not pushed to the serviceFormArray, when I click update without any changes to the checkbox, this.updateServicesForm.value
is empty.
I have to uncheck those checked checkboxes and then check it again so that it will push to the formarray
.
I tried a few changes, but to no avail, can someone suggest what is the correct code to archived what I need? Thank you so much. HTML
<form action="javascript:" [formGroup]="updateSvcForm">
<div class="row" *ngFor="let service of servicesOptions; let i=index">
<div class="col-sm-12">
<div class="checkbox-color checkbox-primary">
<input type="checkbox" id={{service.value}} [value]="service.value" (change)="onCheckChange($event)" [checked]=isSelected(service.value)>
<label for={{service.value}}>
{{service.description}}
</label>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2"></label>
<div class="col-sm-10">
<button class="btn btn-primary m-b-0 ripple light" (click)="updateServices()">Update</button>
</div>
</div>
</form>
Component.TS
sservicesOptions = [
{ description: '1. Sweeping', value: 'sweeping' },
{ description: '2. Mopping', value: 'mopping' },
{ description: '3. Windows', value: 'windows' },
{ description: '4. Washing Clothes', value: 'washingclothes' },
];
this.updateSvcForm= this.fb.group({
sservices: new FormArray([]),
});
onCheckChange(event) {
const sservicesFormArray: FormArray =
this.updateSvcForm.get('sservices') as FormArray;
if (event.target.checked) {
sservicesFormArray.push(new FormControl(event.target.value));
}
else {
let i: number = 0;
sservicesFormArray.controls.forEach((ctrl: FormControl) => {
if (ctrl.value == event.target.value) {
sservicesFormArray.removeAt(i);
return;
}
i++;
});
}
}
isSelected(sserviceOption) {
return this.empDetails.services.indexOf(serviceOption) >= 0;
}
console.log(this.updateSvcForm.value);
}
Data from this.empDetails.services API return
sservices: Array(2)
0: "mopping"
1: "washingclothes"
length: 2
__proto__: Array(0)
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.
However, the data are not pushed to the serviceFormArray, when I click update without any changes to the checkbox, this.updateServicesForm.value
is empty.
I have to uncheck those checked checkboxes and then check it again so that it will push to the formarray
.
I tried a few changes, but to no avail, can someone suggest what is the correct code to archived what I need? Thank you so much. HTML
<form action="javascript:" [formGroup]="updateSvcForm">
<div class="row" *ngFor="let service of servicesOptions; let i=index">
<div class="col-sm-12">
<div class="checkbox-color checkbox-primary">
<input type="checkbox" id={{service.value}} [value]="service.value" (change)="onCheckChange($event)" [checked]=isSelected(service.value)>
<label for={{service.value}}>
{{service.description}}
</label>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2"></label>
<div class="col-sm-10">
<button class="btn btn-primary m-b-0 ripple light" (click)="updateServices()">Update</button>
</div>
</div>
</form>
Component.TS
sservicesOptions = [
{ description: '1. Sweeping', value: 'sweeping' },
{ description: '2. Mopping', value: 'mopping' },
{ description: '3. Windows', value: 'windows' },
{ description: '4. Washing Clothes', value: 'washingclothes' },
];
this.updateSvcForm= this.fb.group({
sservices: new FormArray([]),
});
onCheckChange(event) {
const sservicesFormArray: FormArray =
this.updateSvcForm.get('sservices') as FormArray;
if (event.target.checked) {
sservicesFormArray.push(new FormControl(event.target.value));
}
else {
let i: number = 0;
sservicesFormArray.controls.forEach((ctrl: FormControl) => {
if (ctrl.value == event.target.value) {
sservicesFormArray.removeAt(i);
return;
}
i++;
});
}
}
isSelected(sserviceOption) {
return this.empDetails.services.indexOf(serviceOption) >= 0;
}
console.log(this.updateSvcForm.value);
}
Data from this.empDetails.services API return
sservices: Array(2)
0: "mopping"
1: "washingclothes"
length: 2
__proto__: Array(0)
Share
edited Mar 4, 2019 at 14:05
Devora
asked Mar 4, 2019 at 13:21
DevoraDevora
3574 silver badges15 bronze badges
8
-
how do you fill
this.EmployeeDetails
? can you show more code please – Mohamed Ali RACHID Commented Mar 4, 2019 at 13:33 - Just a normal api return. The return result is as shown in the last part of the post – Devora Commented Mar 4, 2019 at 13:44
-
can you show how do you get it from the api ? are you using
updateServicesForm
in HTML ? (is itupdateSvcForm
?) – Mohamed Ali RACHID Commented Mar 4, 2019 at 13:50 - Yes its updateSvcForm. FormGroupName in html – Devora Commented Mar 4, 2019 at 14:07
- await this.employeeService.getEmployeeDetails(this.Employeeuuid).subscribe((res) => { this.EmployeeDetails = res as Employee[]; – Devora Commented Mar 4, 2019 at 14:07
3 Answers
Reset to default 6 +25The reason for this, is that you are using checked
to mark which checkboxes should be checked, they have no correlation to your form array, so if you do not touch the checkboxes, the formarray will correctly be empty.
I can e up with a couple options to solve this... also the following changes:
change function could be changed to this:
onCheckChange(event) {
if (event.target.checked) {
this.ssArray.push(this.fb.control(event.target.value));
}
else {
this.ssArray.removeAt(this.ssArray.value.findIndex(x => x === event.target.value))
}
}
Doesn't matter how you do it, your way works as well :) I also like using FormBuilder
(here injected as fb
).
I like to use a getter in this case:
get ssArray() {
return this.updateSvcForm.get('sservices') as FormArray;
}
The options I can think of:
- add a
checked
property to the objects in your arraysservicesOptions
- Keep your
isSelected
function, but add the chosen options to your formarray initially
I like option 1 best, so add a checked
property to the objects:
servicesOptions = [
{ description: '1. Sweeping', value: 'sweeping', checked: false },
{ description: '2. Mopping', value: 'mopping', checked: false },
{ description: '3. Windows', value: 'windows', checked: false },
{ description: '4. Washing Clothes', value: 'washingclothes', checked: false },
];
Then when you build the form, change the checked status for those that should be preselected, and also add the values that should be checked to your form array:
constructor(private fb: FormBuilder) {
this.updateSvcForm = this.fb.group({
sservices: this.fb.array([]),
});
// change the checked status for the checkboxes that should be checked
this.servicesOptions.map(x =>
this.empDetails.services.indexOf(x) >= 0 ? x.checked = true : x.checked = false)
// initially set the selected form controls to the formarray
this.empDetails.services.map((x) => this.ssArray.push(this.fb.control(x)))
}
Then you can add [checked]="service.checked"
in template.
DEMO
Option 2:
Keep your checked
function like you have, just remember to add the prechosen values to your formarray. I don't really like this option, since for example, we end up calling a function in template, which is really not remended. But anyway, keep the code as the same as you have now, just add the intial values to the formarray:
this.updateSvcForm = this.fb.group({
sservices: this.fb.array([]),
});
// add the intial values to the formarray:
this.empDetails.services.map((x) => this.ssArray.push(this.fb.control(x)))
DEMO
I added a console.log inside the function, to show how it is called. It's okay for a demo like this, but if you have a big form, I would really caution for you to use this solution.
There would be a third option, to actually set all values to your form array, and then just toggle the boolean value of the checkbox, but that would require some refactoring of code, which I don't know if you want. But there is that option too.
you forget to set the new value of the form Array sservices
:
onCheckChange(event) {
const sservicesFormArray: FormArray =
this.updateSvcForm.get('sservices') as FormArray;
if (event.target.checked) {
sservicesFormArray.push(new FormControl(event.target.value));
}
else {
let i: number = 0;
sservicesFormArray.controls.forEach((ctrl: FormControl) => {
if (ctrl.value == event.target.value) {
sservicesFormArray.removeAt(i);
break;
}
i++;
});
}
// set the new value of sservices form array
this.updateSvcForm.setControl('sservices', sservicesFormArray);
}
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 bees like
<form *ngIf="updateSvcForm" [formGroup]="updateSvcForm" (submit)="submit(updateSvcForm)">
<div formArrayName="sservices">
<div *ngFor="let control of updateSvcForm.get('sservices').controls;let i=index">
<input type="checkbox" [formControlName]="i"/>
{{sservicesOptions[i].description}}
</div>
</div>
<button type="submit">submit</button>
</form>
{{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: `
<ng-container *ngFor="let item of source;let i=index;let last=last">
<div [ngClass]="last?'form-group':''" class="form-check" >
<input type="checkbox" class="form-check-input" id="{{_name+''+i}}"
[ngModel]="_selectedItems[i]"
(ngModelChange)="setValue($event,i)" (blur)="onTouched()" >
<label class="form-check-label" for="{{_name+''+i}}">{{item[_col]}}</label>
</div>
</ng-container>
`,
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 mas) 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 mas
booleanToProps(propsBoolean: boolean[]) {
let props: any[] = [];
if (propsBoolean) {
propsBoolean.forEach((item, index) => {
if (item)
props.push(this.source[index][this._key])
})
}
return props;
}
}
本文标签: javascriptbinding data to angular checkboxStack Overflow
版权声明:本文标题:javascript - binding data to angular checkbox - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741691288a2392737.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论