Patch the values in angular form

2019-02-26 06:02发布

问题:

I am making an application using angular 6, where i am using angular dynamic form.

As of creating the form and submitting i have completed everything, You could able to see the working stackblitz, https://stackblitz.com/edit/angular-x4a5b6-hne6fg

FYI: This form has children elemts which will gets opened and also gets append on click the add button and removes one by last on click the remove button..

The thing i am in the need is, i just need to patch the values to each inputs via service during edit of each row respectively..

Say i call the get service as,

    this.dynamicFormService.getRest(url,token).subscribe(res => {
        this.form.value = res.data;   //Tried this line but got error (Cannot assign to 'value' because it is a constant or a read-only property)
     })

I am trying to patch the values to the form which you can see in dynamic-form.ts in stacblitz,

  ngOnInit() {
    this.form = this.qcs.toFormGroup(this.questions);
}

If i console the the form as console.log(this.form.value), it gives the result as,

template_desc: ""
template_name: ""
template_properties: Array(0)
length: 0
__proto__: Array(0)

And console.log(res.data) in get service result gives,

data:
template_obj_id: "123"
template_desc: "New template description"
template_name: "New Template"
template_properties: Array(1)
0:
property_name: "Property name"
property_origin: ["VBM"]
property_required: true
property_type: "Property type"

I just need to bind this data that comes from res.data to get patched to all the form inputs.

These are the values i need to patch to all the inputs respectively like template_name has the value as New Template and template_desc as New template description etc..

And also an another important thing for template_properties value which is array will get open on click add button during create but whereas during edit it should get opened automatically with number of rows present inside template_properties..

Though the question went little bigger for explanation, the thing i am in the need is single.. I need to patch the data comes from service (res.data) to each form elements respectively and all the form elements including template_properties needs to be visible in which user can edit and submit the form.

The screenshot of res.data,

Tried with the below answer,

 this.form.patchValue({
      template_desc: res.data.template_desc,
      template_name:  res.data.template_name,
    });

But if i give template_properties: res.data.template_properties, the template properties not getting binded but other two template name and desc getting binded..

Kindly help to achieve the patching of data to the form elements...

回答1:

in https://stackblitz.com/edit/angular-x4a5b6-xcychx?file=src%2Fapp%2Fdynamic-form.component.html, I put as "path the values".

The only thing to take account is that you need add to the form as element as has the array.

  //This function serve to add to the array the elments of the arrays
  //Is the same that we use when add a new line to the Array Form
  addControls(control: string) {
    let question: any = this.questions.find(q => q.key == control);
    let children = question ? question.children : null;
    if (children)
      (this.form.get(control) as FormArray).push(this.qcs.toFormGroup(children))
  }
  this.dynamicFormService.getRest(url,token).subscribe(res => {
        //In res.data you have the data.
        //In res.dat.template_properties you has the array

        //Create the Form
        this.form = this.qcs.toFormGroup(this.questions);
        //Add so element at array as was necesary
        for (let i=0;i<this.data.myArray.length;i++)
        {
            this.addControls('template_properties')
        }
        //Finally use patchValue
        this.form.patchValue(res.data);
    }

Well, your data not corresponding with this.questions and there are a problem with your property_origin (about the problem with as convert propertyOrigin to a series of checkBox, let me some time -I'll updated this anwser more later-)

Updated see stackblitz updated Creating a new Question Type to create a list of checkBox There're a problem with the checkBox. In Angular a checkBox can be have only two values "true" or "false". Until I know, there no effect the attrib value.

So, we can not make something like

<input type="checkbox" value="car" formControlName="checkName">
<input type="checkbox" value="dog" formControlName="checkName">

To get in "checkName" an array ['car','dog'] if you checked the two values.

How we can solutionated this? I think that the better idea is has a formGroup like

<div formGroupName="checkName">
  <input type="checkBox" formControlName="car">
  <input type="checkBox" formControlName="dog">
</div>

So, we'll get some like

   checkName={car:true,dog:false}

Well one work is make a new QuestionType "ListCheckBoxQuestion"

export class ListCheckBoxQuestion extends QuestionBase<string> {
  controlType = 'list-chekbox';
  options: {key: string, value: string}[] = [];

  constructor(options: {} = {}) {
    super(options);
    this.options = options['options'] || [];
  }
}

After we can change the dinamic-formquestion.component adding a new switch. See that we create a formGroupName with the question.key, and inside this group we create a formControlName using "options"

<div [formGroupName]="question.key"  *ngSwitchCase="'list-chekbox'" >
  <ng-container *ngFor="let opt of question.options">
  <input type="checkbox"  [formControlName]="opt.key">
                <label >{{opt.value}}</label>
  </ng-container>
</div>

Well, the hard part is wen we create the form (the toFormGroup function of question-control-service

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

    questions.forEach(question => {
      if (question.controlType=="array") {
         group[question.key]=new FormArray([]);
      }else if (question.controlType=="list-chekbox")
      { //We create a FromGroup using the options
        let controls:any={};
        for (let option of question.options)
             controls[option.key]=new FormControl(false);
        group[question.key]=new FormGroup(controls);
        console.log("*",question);
      }
      else {
       ...
      }
    });
    return new FormGroup(group);
  }

Well there're a job to to that is convert our form.value to data and the data received to form.value.

When we received some like ["car","dog"] we must transform to {car:true,dog:true} and when we have {car:true,dog:false} we must transform to ["car"]. We must use map

Update Take a look to the function toFormGroup of question-control-service. This function is the function that create the form. In the last version we add so many lines to the array as values length. Change this funcion to add a linea if no value is indicated

questions.forEach(question => {
      if (question.controlType == "array") {
        group[question.key] = new FormArray([]);
        if (question.value) {
          if (question.children) {
            //If question.children we must to give "value" to the children
            //So the children has as value, the value given in dataArray
            for (let value of question.value) {
              Object.keys(value).forEach(key => {
                let qu = question.children.find(x => x.key == key)
                if (qu) {
                  qu.value = value[key];
                  qu.controlType = qu.elementType
                }
              })
              group[question.key].push(this.toFormGroup(question.children))
            }
          }
        } //Add this lines to add one line to the array if no 
          //value is indicated
    else {
      if (question.children) {
        question.children.forEach(qu => {
          qu.controlType = qu.elementType

        })
        group[question.key].push(this.toFormGroup(question.children))
      }
    }


回答2:

Use patchValue to set the data to form

this.dynamicFormService.getRest(url,token).subscribe(res => {
   this.form.patchValue({
      template_desc: res.data.template_desc,
      template_name:  res.data.template_name,
    });
 }