2

I am making angular 6 application where i am using Angular dynamic form

Here i have made a nested input fields in which there will be two input textboxes at the initial stage and on click the add button the next two input boxes will get append on each click on add button.

Everything works fine here regarding it..

Here i have used the values in question-service.ts as,

  new TextboxQuestion({
  elementType: "textbox",
  class: "col-12 col-md-4 col-sm-12",
  key: "project_name",
  label: "Project Name",
  type: "text",
  value: '',
  required: true,
  order: 1
  }),

  new TextboxQuestion({
  elementType: "textbox",
  class: "col-12 col-md-4 col-sm-12",
  key: "project_desc",
  label: "Project Description",
  type: "text",
  value: '',
  required: true,
  order: 2
  }),
  new ArrayQuestion({
    key: 'myArray',
    value: '',
    order: 3,
    children: [
      new TextboxQuestion({
      elementType: "textbox",
      class: "col-12 col-md-4 col-sm-12",
      key: "property_one",
      label: "Property One",
      type: "text",
      value: '',
      required: true,
      order: 3
      }),
      new TextboxQuestion({
      elementType: "textbox",
      class: "col-12 col-md-4 col-sm-12",
      key: "property_two",
      label: "Property Two",
      type: "text",
      value: '' ,
      required: true,
      order: 4
      })
    ]
  })

Which i need to change like the data should be from json for each like,

  jsonData: any = [
    {
      "elementType": "textbox",
      "class": "col-12 col-md-4 col-sm-12",
      "key": "project_name",
      "label": "Project Name",
      "type": "text",
      "value": "",
      "required": true,
      "order": 1
    },
    {
      "elementType": "textbox",
      "class": "col-12 col-md-4 col-sm-12",
      "key": "project_desc",
      "label": "Project Description",
      "type": "text",
      "value": "",
      "required": true,
      "order": 2
    },
    {
      "elementType": "array",
      "key": "myArray",
      "value": "",
      "order": "3",
      "children": [
        {
          "elementType": "textbox",
          "class": "col-12 col-md-4 col-sm-12",
          "key": "property_one",
          "label": "Property One",
          "type": "text",
          "value": "",
          "required": true,
          "order": 3
        },
        {
          "elementType": "textbox",
          "class": "col-12 col-md-4 col-sm-12",
          "key": "property_two",
          "label": "Property Two",
          "type": "text",
          "value": "",
          "required": true,
          "order": 4
        }
      ]
    }
  ];

Stackblitz without JSON:

https://stackblitz.com/edit/angular-x4a5b6-xcychx

Stackblitz with JSON:

https://stackblitz.com/edit/angular-x4a5b6-u6ecpk

The same scenario which happens in stacblitz link without json needs to happen while loading JSON..

I have given the following inside getQuestions() like,

 getQuestions() {

    console.log(this.jsonData);

    let questions: any = [];

    this.jsonData.forEach(element => {
      if (element.elementType === 'textbox') {
        questions.push(new TextboxQuestion(element));
      } else if (element.elementType === 'array') {
        questions.push(new ArrayQuestion(element));
      }
    });

    return questions.sort((a, b) => a.order - b.order);
  }
}

For normal textbox its working but whereas for the children its not working on clicking add button (textboxes were not displayed), the child textboxes not getting added.

Kindly help me to achieve the same result that happens in link 1 also needs to happen while using JSON in link 2 .. And also kindly dont include any third party libraries inside everything is going in core angular.

Maniraj Murugan
  • 8,868
  • 20
  • 67
  • 116

2 Answers2

3

@Many, when you have type array, you must create the children before push the array.

...
} else if (element.elementType === 'array') {
    let children:any[]=[]; //declare children
    //each children of element fill our array children
    element.children.forEach(e=>{
       if (e.elementType === 'textbox') {
         children.push(new TextboxQuestion(e));
       }
    })
    //Hacemos un push not of element else element + the property children
    //changed (it will be the array created)
    questions.push(new ArrayQuestion({...element,children:children}));
}
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • Thank you @Eliseo, Kindly provide stackblitz please.. Its throwing the error as ```Error: Cannot read property 'push' of undefined``` – Maniraj Murugan Nov 02 '18 at 07:29
  • :glups: is let children:any[]=[] //<--it's necesary declare the variable as empty array. just corrected in code – Eliseo Nov 02 '18 at 08:01
  • Hi Eliseo, I have added checkbox in the dynamic form, its not giving the value i have given inside options, rather it is displaying the boolean as value either true or false, if check or uncheck respectively.. Kindly help me to get the value we give in options while using checkbox .. Dynamic form with checkbox link https://stackblitz.com/edit/angular-x4a5b6-sspsyx – Maniraj Murugan Nov 07 '18 at 14:10
  • checked or radio? if you have options must be< input type="radio" ...> NOT < input type="checked"> – Eliseo Nov 07 '18 at 19:23
  • Then how should i need to do for checkbox in dynamic form?? Here i will have a checkbox where if the user check, then the value of checked one needs to be displayed. Here i am having only checkbox not radio button.. – Maniraj Murugan Nov 08 '18 at 05:19
  • @Mani, be carefull, I miss when display "label", check the corrected answers – Eliseo Nov 08 '18 at 08:22
  • Regarding label, How can i generate different label name for each button click because if i click any input box, then the first ```property_one``` alone getting focused because the ```[id]="property_one"``` and ``` – Maniraj Murugan Nov 08 '18 at 08:35
  • you can replace all **question.key** by **index?question.key+index:question.key** and create a Input() index -I've changed the stackblitz to add this modification- – Eliseo Nov 08 '18 at 09:33
  • Thanks @Eliseo.. As of label i have got clear now.. But in validation, i am having a doubt.. Here i have given just required message to display if ```!isValid``` but i have to show ```minlength``` and ```maxlength``` message separately.. How can i make it?? – Maniraj Murugan Nov 08 '18 at 10:30
  • I have tried with, ```
    {{question.label}} is required
    Minimum {{question.minlength}} is required
    ``` But nothing happens..
    – Maniraj Murugan Nov 08 '18 at 10:30
  • Stackblitz with minlength and maxlength attributes, https://stackblitz.com/edit/angular-x4a5b6-bdtqch – Maniraj Murugan Nov 08 '18 at 10:37
  • If you look here, kindly see the below solution comment also.. I am having good help from you regarding dynamic form.. Please help me to fix it.. – Maniraj Murugan Nov 08 '18 at 12:48
  • I update my second answer (and the link to the stackblitz) , try you add the conditions to add the validators min and max) – Eliseo Nov 08 '18 at 19:18
  • Kindly verify the second answer stackblitz whether it is updated because i couldn't see any changes in it regarding validation.. – Maniraj Murugan Nov 09 '18 at 03:58
  • Hi Eliseo, Here is the another new question for you https://stackoverflow.com/questions/53220425/patch-the-values-in-angular-form for patching of data to those form elements during edit.. As you alone know the way i am going in this angular dynamic form, i couldn't get a good help from others.. Hope you understand the question for editing the data and hence you can help me for the same.. – Maniraj Murugan Nov 09 '18 at 07:26
  • Eliseo, Here is another question for you in our dynamic form, https://stackoverflow.com/questions/53261208/how-to-assign-data-from-service-to-json – Maniraj Murugan Nov 12 '18 at 11:50
1

You must add a new type "check-box"

export class CheckBoxQuestion extends QuestionBase<string> {
  controlType = 'checkbox';
  type: boolean;

  constructor(options: {} = {}) {
    super(options);
  }
}

And change the dinamic Form question

<div [formGroup]="form">
    <!--the label only show if it's NOT a checkbox --->
    <label *ngIf="question.controlType!='checkbox'" [attr.for]="question.key">{{question.label}}</label>

  <div [ngSwitch]="question.controlType">
    ...
    <!--add type checkbox-->
    <ng-container *ngSwitchCase="'checkbox'">
    <input  [formControlName]="question.key" type="checkbox"
            [id]="question.key" >
                <label [attr.for]="question.key">{{question.label}}</label>

            </ng-container>
    ...
  </div> 

And question service to take account the new checkbox

else if (e.elementType === 'checkbox') {
            children.push(new CheckBoxQuestion(e));
          }

Update if we want to add more validators, take a look at the function "toFormGroup" of question.service.ts

toFormGroup(questions: QuestionBase<any>[]) {
    let group: any = {};

    questions.forEach(question => {
      if (question.controlType=="array") {
         group[question.key]=new FormArray([]);
      }
      else {
        //create an array of "validators"
        let validators:any[]=[];
        //If question.required==true, push Validators.required
        if (question.required && question.controlType!='checkbox')
            validators.push(Validators.required);
        //...add here other conditions to push more validators...
        group[question.key] = new FormControl(question.value || '',validators);
      }
    });
    return new FormGroup(group);
  }

Update two it's necesary change too the questionbase.ts to add this properties

export class QuestionBase<T> {
  value: T;
  ...
  maxlength:number;
  minlength:number;

  constructor(options: {
      value?: T,
      ....
      minlength?:number,
      maxlength?:number,
      controlType?: string,
      children?:any
    } = {}) {
    this.value = options.value;
    ....
    this.minlength = options.minlength;
    this.maxlength = options.maxlength;
    ...
  }
}

For see the errors you must as about form.get(question.key).errors, e.g

  <div class="errorMessage" 
     *ngIf="form.get(question.key).errors?.required">
    {{question.label}} is required
  </div>

Tip: for know about your errors use

{{form.get(question.key).errors|json}}

See the forked stackblitz

Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • Why its returning boolean instead of the string which was given as value in JSON? The checkbox may be a multiple checkboxes but in general it will return only value know?? https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_input_checked – Maniraj Murugan Nov 08 '18 at 08:23
  • kindly help me in these two scenarios 1) Though i use the checkbox that returns boolean either true or false, at the initial stage i have given ```[checked]="true"``` but i couldn't see the value as ```true``` for ```property_check```, but the checkbox is checked.. Whereas if i uncheck it then the value is setting to ```false``` and if i check it then change to ```true``` but at initial ```checked``` value is empty.. 2) Minlength and maxlength validation message not displaying (refer above solution last comment).. – Maniraj Murugan Nov 08 '18 at 12:46
  • About the checks NOT use [checked] if you see the stackblitz, you see that the value is "" (at first)- you can avoid when create the control give a value false if is a checkbox-, and true or false when you check the checkbox – Eliseo Nov 08 '18 at 19:53