ReactiveForms com modelos recursivos dinâmicos

8

Aqui está o meu problema.

Exemplo on-line do problema

Eu tenho um JSON dinâmico que preciso converter em um formulário. Então, usei formulários reativos e, passando por todas as propriedades do JSON, crio um FormGroup ou FormControl, desta maneira:

sampleJson ={prop1:"value1", prop2: "value2",...}

...

  myForm: FormGroup;
  myKeys=[];
    ...

  ngOnInit() {
    this.myForm = this.getFormGroupControls(this.sampleJson, this.myKeys);

  }

getFormGroupControls(json:any,keys): FormGroup{
    let controls = {};
    let value = {};

    for (let key in json) {
      if (json.hasOwnProperty(key)) {

        value = json[key];
        if (value instanceof Object && value.constructor === Object) {

          keys.push({"key":key,children:[]});
          controls[key] = this.getFormGroupControls(value,keys[keys.length-1].children);
        } else {

          keys.push({"key":key,children:[]});
          controls[key] = new FormControl(value);

        }
      }
    }

    return new FormGroup(controls);
  }

Depois de fazer isso, uso modelos recursivos para criar o formulário; se não usar modelos recursivos, faço o formulário funcionar. No entanto, com modelos recursivos, estou recebendo erros:

<form [formGroup]="myForm">

  <div class="form-group">


    <ng-template #nodeTemplateRef let-node>

      <div class="node">
        <div  *ngIf="node.children.length">
          {{"section [formGroupName]="}} {{ getNodeKey(node) }}
          <section style="display:block;margin:20px;border:solid 1px blue;padding-bottom: 5px;"
            [formGroupName]="getNodeKey(node)" >
            <h1>{{ node.key }}</h1>
            <ng-template
              ngFor
              [ngForOf]="node.children"
              [ngForTemplate]="nodeTemplateRef">
            </ng-template>
          </section>
          {{"end of section"}}
        </div>
        <div  *ngIf="!node.children.length">
          <label [for]="node.key">{{node.key}}</label>&nbsp;
          <input  type="text" [id]="node.key"
                  class="form-control">
        </div>
      </div>

    </ng-template>

    <ng-template *ngFor="let myKey of myKeys"
                 [ngTemplateOutlet]="nodeTemplateRef"
                 [ngTemplateOutletContext]="{ $implicit: myKey   }">
    </ng-template>

  </div>

FormerComponent.html: 25 ERRO Erro: Não é possível encontrar o controle com o nome: 'road'

Isso corresponde a este exemplo JSON:

"address": {
        "town": "townington",
        "county": "Shireshire",

        "road": {
          "number": "1",
          "street": "the street"
        }

Eu tenho está sendo exibido, então eu sei que os elementos estão lá. o que estou perdendo?

Dalorzo
fonte
Eu acredito que [formGroupName]="road"não está ciente de que está aninhado no grupo de addressformulários. Ele está procurando um grupo de formulários chamado roaddiretamente sob a raiz [formGroup]="myForm". Se você aninhar um grupo de roadformulários diretamente abaixo myForm, verá que o erro não aparece mais.
Alex K
Substituir formGroupNamepor formGroupqualquer lugar pode corrigir o problema. Mas você precisará de uma maneira de capturar a FormGroupinstância correta para cada grupo aninhado.
Alex K
que cria esse outro erro> Não é possível criar a propriedade 'validator' na string 'name'
Dalorzo 16/01
o json dinâmico sempre retornará um conjunto conhecido? isso pode mudar, mas podemos conhecê-los e ter algo do tipo seguro?
maxime1992
Acho que o que estou pedindo é: é verdadeiramente dinâmico ou é apenas uma oneOfde um conjunto conhecido de possíveis entradas como name, personal, addressetc
maxime1992

Respostas:

2

Ou se você ainda deseja hierarquia de grupos / controles de formulário, pode usar as diretivas formGroup e formControl passando-as recursivamente (em vez de formGroupName e formControlName)

Link Stackblits

NB: mesmo problema aqui: Recursive ReactiveForm não pode encontrar formGroups dentro do modelo

Neji Sghair
fonte
1
este +1 é uma solução impressionante ..
Dalorzo 27/01
Este acabou funcionando muito melhor e exigiu menos alterações no meu objetivo original. É por isso que concedo a resposta, pois acho que é mais preciso
Dalorzo 28/01
9

O problema com o código atual parece ser que o pai do modelo ng é o componente do aplicativo, portanto, ele não leva em conta outros formGroupNames nos principais modelos que você definiu e sempre procura no FormGroup raiz.

Parece também que o nome completo do grupo / nome do controle não é suportado nos modelos (por exemplo, não é possível usar formGroupName="address.road")

Se você precisar, por algum motivo, de formGroups - poderá passá-los em contexto para modelos. Ou você pode endereçar formControls diretamente:

  • remover tudo formGroupNamedo modelo
  • armazenar fullPath: keys.push({"key":key,children:[], fullKey: parent ? parent.fullKey + '.' + key: key});(você pode armazenar a FormControlprópria instância também ofc.)
  • e use-o: <input type="text" [formControl]="myForm.get(node.fullKey)"

Exemplo Stackblitz

Petr Averyanov
fonte