1

I've been trying many different methods have been failing all week, I need to be able to associate a checkbox control with an answer.

Pretty much, if the checkbox is checked then user must answer the question and it must have a validation of minlength 4.

The checkbox will contain a question and answer.

So if the user chooses that question he/she must provide an answer.

the questions are rendered from the server in an object such as;

 { 
                question_id: "1",
                selected: true,
                EN: "Question 1 - EN",
                FR: "Question 1 -FR",

            },
            { 
                question_id: "2",
                selected: false,
                EN: "Question 2 - EN",
                FR: "Question 2 -FR"
            }

I can post my code if required, it is however very long and complicated.

enter image description here

Taranjit Kang
  • 2,510
  • 3
  • 20
  • 40
  • Can you convey what the problems you have are? 'The checkbox will contain a question and answer.' - I take it you mean some sort of component will contain a checkbox, that if selected will control display of input which you can control with an *ngIf. ReactiveFormControl are recommended for validation capabilities https://angular.io/guide/reactive-forms https://angular.io/api/forms/Validators – JGFMK Jul 23 '17 at 22:00
  • Hey, I've added an image of what I am doing. Essentially, when I retrieve the questions from the server I need to: 1. Display the questions along with checkboxes and answer input control 2. Have inline validation with min length(4) 3. There are 20 questions, you must choose 5. The issues I'm having is that, when I choose 5, the validations are still triggered for the ones unselected. I tried adding validators manually, by .setValidators and .clearValidators on the ones that are not selected. – Taranjit Kang Jul 23 '17 at 22:05
  • When I check 5 questions and answer them and go to the next page, they should be stored as object. When I hit back from the next page, the same questions answered should be displayed with the checkboxes. I am able to do this, but the answers populating the answercontrol(text input) do not trigger the validation of min length 4, also the unselected checkboxes I believe also trigger the validation thus not allowing me to submit again. – Taranjit Kang Jul 23 '17 at 22:09
  • Maybe take a look here.. Will follow up tomorrow.. https://stackoverflow.com/a/33377290/495157 – JGFMK Jul 23 '17 at 22:17

1 Answers1

2

If you create a Reactive Form you can dynamically change the validation

component

this.questionForm = fb.group({
  questions: fb.array(this.questions.map(this.createQuestionControl(fb)))
});

createQuestionControl(fb: FormBuilder) {
  return (question, index) => {
    const checkbox = question.selected
    const answerbox = question.selected ? ['', [Validators.required, Validators.minLength(4)]] : ''
    return fb.group({question: checkbox, answer: answerbox, questionNumber: index + 1});
  }
}

changeValidator(selected, index) {
  const answerbox = this.questionForm.get('questions.' + index).get('answer')

  const validators = selected ? [Validators.required, Validators.minLength(4)] : null
  answerbox.setValidators(validators);
  answerbox.updateValueAndValidity();
}

The createQuestionControl() method will change each question into a control as below which the form builder can turn into a group with a question and an answer

{ question: true, answer: ['', [Validators.required, Validators.minLength(4)]], index: 4 }

The changeValidator() method will add or remove validators on the answer if the question is changed (note: do not forget updateValueAndValidity)

template

<form [formGroup]="questionForm" (ngSubmit)="submit(questionForm)">

  <div formArrayName="questions">
    <div *ngFor="let question of questionForm.get('questions').controls | orderBySelected; let i = index;" [formGroupName]="i">
      <!--{{questionForm.get('questions.' + i + '.questionNumber').value}}-->
      {{questions[questionForm.get('questions.' + i + '.questionNumber').value - 1]['EN']}}
      <input type="checkbox" formControlName="question" (ngModelChange)="changeValidator($event, i)"/>
      <input type="text" formControlName="answer" />
      <em *ngIf="questionForm.get('questions.' + i + '.answer').invalid">Minimum length 4</em>
    </div>
  </div>

  <button type="submit" [disabled]="questionForm.invalid">Submit</button>

</form>

Following a clarification in the comments:

Maximum of 5 can be checked at a given time

I have updated the array to have cross field validation of no more than 3 (easier to test you can change it to 5)

export function max3Selected(formArray) {

  let totalSelected = formArray.controls.reduce((selectedControls, control) => 
  {
    if (control.get('question').value) {
      selectedControls++
    }
    return selectedControls;
  }, 0)

  return totalSelected > 3 ? { moreThanThreeSelected: true } : null;
}

and you would change the fb.array to include the validator function

fb.array(this.questions.map(this.createQuestionControl(fb)), max3Selected)

Screenshot of result

enter image description here

Live plunker example

0mpurdy
  • 3,198
  • 1
  • 19
  • 28
  • Maximum of 5 can be checked at a given time, I'm working with your solution -- But It allows me to check more than 5. – Taranjit Kang Jul 23 '17 at 23:00
  • Also I noticed, when you Select a checkbox -- if you console.log(question) the property is always selected: true, even when you uncheck -- but the checkbox gets unchecked, property selected is still true; – Taranjit Kang Jul 23 '17 at 23:25
  • I am asking because my answer input is displayed whether or not question is checked. Using animations. this does not work however, question.selected is always true until submitted. – Taranjit Kang Jul 23 '17 at 23:29
  • I believe your plunkr example of max 5 selected, only allows me to select 1 and submit -- It may be reversed? – Taranjit Kang Jul 23 '17 at 23:53
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/149949/discussion-between-0mpurdy-and-taranjit-kang). – 0mpurdy Jul 23 '17 at 23:54
  • How can i associate the question # eith the answer? This associates the question control which is either false or true with answer. I need to be able to return to the page from the nect screen and associate thr answer to the same question again – Taranjit Kang Jul 24 '17 at 02:01
  • If you look at the group in the form value that is submitted `{ "question": true, "answer": "sdfsdf", "index": 4 }` then you can use `index + 1` to get the question number – 0mpurdy Jul 24 '17 at 06:47
  • Only works if the questions are always in order from 1-20, will not work if they are sorted based on selected to bubble them to the top – Taranjit Kang Jul 24 '17 at 11:32
  • Because the indexes are assigned when the controls are created the order doesn't matter - you can check it on the example to see that, I'll change it to `questionNumber` to avoid confusion – 0mpurdy Jul 24 '17 at 12:00
  • The actual questions wont be question 1, question 2, theybwill be questions. But they have question_ids such as 1, 2 .. etc such as EN and FR are the actual questions – Taranjit Kang Jul 24 '17 at 12:12
  • I will try it once I am at work and see if i can make it work – Taranjit Kang Jul 24 '17 at 12:15