-1

i'm trying to get values from multiple checkbox here https://stackblitz.com/edit/angular-ivy-uahtjx i try this approach but didn't with for me https://stackblitz.com/edit/quiz-answer-value i think it's because the JSON schema is different, first one is array, and the second one is object.

i set up my html like this

<div *ngFor="let option of form.options">
  <label><input (change)="onChange(i,j, form.code,check.checked)" #check 
  [value]="answers[i].value.forms[j].answer.toString().indexOf(form.code)>=0" formControlName="answer" type="checkbox">{{option}}</label>
</div>

and for my change function like this

onChange(indexi: number, indexj: number, code: string, isChecked: boolean) {
  const control = this.answerArray.at(indexi).get("forms")['controls'][indexj].get('answer');
  console.log(control);
}

can someone help me?

naoval
  • 338
  • 6
  • 29

1 Answers1

2

Thre're several errors in your code.

The last stackblitz you indicate (that gone from this SO) is an "imaginative" way to mannage a series of check-boxes to feed a formControl that store an array of strings. The idea is that the input has [checked] and (change), but NOT formControlName (it's the reason because at first the check-boxes became "checked"). The formControl has no input in the form. Yes, the formControl exist, but has no input.

The [checked] of the check-box is a function that indicate if the "option" is include in the value of the control (using indexOf)

The (change) has as arguments, the indexes -to localize the formControl-, the value of the "option" and if is checked or not.

A more simple stackblitz to show this concept:

<!--see that "answer" has no input, it's only a variable in .ts-->
<div *ngFor="let option of options">
<input #check type="checkbox"
  [checked]="answer.indexOf(option)>=0"
  (change)="onChange(option,check.checked)"
>
{{option}}
</div>

  answer=["check 2"]
  options=["check 1","check 2","check3"]
  onChange(option:string,checked:boolean)
  {
     if (checked && this.answer.indexOf(option)<0)
         this.answer=[...this.answer,option]
         .sort((a,b)=>this.options.indexOf(a)>this.options.indexOf(b)?1:-1)
    if (!checked)
          this.answer=this.answer.filter(x=>x!=option)
  }

Well, in your code

1.-Change the form (please, see the comments in the code)

    <div *ngIf="form.type == 'checkbox'">
        <p>{{form.title}}</p>
        <div *ngFor="let option of form.options">
           <!--in your code WRONG (change)="onChange(i,j, form.code,check.checked)" -->
           <!-- see that we need send the "option", not the form.code-->
            <label><input #check (change)="onChange(i,j, option,check.checked)"
           <!--use checked, not value, and remove the "toString"-->
             [checked]="answers[i].value.forms[j].answer.indexOf(option)>=0"
              <!--see that we has no formControlName="answer" -->
              type="checkbox">{{option}}</label>
        </div>
    </div>

The function onChange, it's simply if checked=true add the "code" and if checked=false remove the "code". Well, there are a dificult that is "sort" the responses. For sort the response, we compare the indexOf the value in the "options"

I use an auxiliar variable "options" to get the "options"

2.-Change your function onChange by

  onChange(indexi: number, indexj: number, code: string, isChecked: boolean) {
    const control = this.answerArray.at(indexi).get("forms")["controls"][indexj]
      .controls.answer;
    
    const options=this.listFormField[indexi].sectionItems[indexj].options
    //options will be, eg. ["check 1","check 2","check 3"]
    if (isChecked && control.value.indexOf(code) < 0)
      control.setValue(
        [...control.value, code].sort((a: string, b: string) =>
          options.indexOf(a) >options.indexOf(b)
            ? 1
            : -1
        )
      );

    if (!isChecked && control.value.indexOf(code) >= 0)
      control.setValue(control.value.filter(x => x != code));
  }

NOTE: You need revised the code, Actually, when you create the form, all yours "answers" are arrays (only need to be array the "answer" that they are ansers of a check-boxs)

Updated To avoid errors if answer is null, we can or avoid when create the form or, another option, change the code in checked as

[checked]="(answers[i].value.forms[j].answer || []).indexOf(option)>=0"

To path value we can, if only is an element, e.g.

const i=0
const j=1
this.response.get('answers.'+i+'.forms.'+j+'.answer')
     .patchValue(["check 1","check 2"])

Yes, to get an "answer" we can use "get" using the index. Perhafs this makes understand because when we use a formArray of formGroup, we write [formGroupName]="i". If we want to make a pathValue over a FormGroup, we need take account, that if we make pathValue over an array, pathValue NOT add elements to the formArray, so if we send more elements, this it's not added. futhermore, if we send less elements, pathValue, remove the elements

take account this, we can also make,e.g.

this.response.get('answers.'+i+'.forms')
     .patchValue(
        [
          {answer:"radio 2"},
          {answer:["check 1","check 2"]},
          {answer:"text input"},
          {answer:"text area"},
          {answer:["check 6"]},
         ]
        )  
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • i actually working this10 hours ago and i found the solution based on my stackblitz code, just playing and i got the result that i wanted, and it's similar to you final code, but i don't add the `[checked]="answer.indexOf(option)>=0" ` is that okay? cause it seems work without it on my apps – naoval Sep 09 '20 at 01:46
  • and `control.value.indexOf` to me it gives me an error said that it's not a function, so i changed it to this `control.value.toString().indexOf` – naoval Sep 09 '20 at 01:49
  • the error is provoceted if "anwser" is null. It's correct conver to string or use `[checked]="(answer || []).indexOf(option)>=0"` -the "answer || []) main that if answer is null or undefined or "", take the value of an empty array – Eliseo Sep 09 '20 at 06:06
  • i see thats why u dont need toString – naoval Sep 09 '20 at 16:46
  • answer updated! :). BTW, in your code, after create the form, you neen't use the two loops to path, value. See that you give the value when create the form – Eliseo Sep 10 '20 at 06:34
  • thanks for the update, i finally have final code, well sort of. I call patchValue using for loop, its just because i have for loop to set custom value to answer, since i have that, i think i just added patchValue there, cause i need to check whether it needs to be a string or an array. – naoval Sep 10 '20 at 09:47
  • cause in my backend, i need to pass a string to `answer`, but since i have an array of `checkbox` so i need to convert that array to string and to display it again using patchValue, `answer` from api will only gives string, but i need an array of checked checkbox right? that's why i use that for loop – naoval Sep 10 '20 at 09:50