2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Angularで、階層構造を持つJSONから入力フォームを動的に生成する

Last updated at Posted at 2019-12-13

Angularで階層構造を持つJSONから入力フォームを動的に生成するコードを、GitHubで公開しました。

GitHubはこちら
dynamic-form-builder-recursive

画面サンプル

※インデントで階層構造を表しています

入力フォームの設定用JSON

  fields: any[] = [
      {
          "id": "text1",
          "text": "text1",
          "type": "text"
      },
      {
          "id": "radio1",
          "text": "radio1",
          "type": "radio",
          "options": [
              {
                  "text": "option1",
                  "value": "option1"
              },
              {
                  "text": "option2",
                  "value": "option2"
              },
              {
                  "text": "option3",
                  "value": "option3"
              }
          ]
      },
      {
          "id": "checkbox1",
          "text": "checkbox1",
          "type": "checkbox",
          "options": [
              {
                  "text": "option1",
                  "value": "option1"
              },
              {
                  "text": "option2",
                  "value": "option2"
              },
              {
                  "text": "option3",
                  "value": "option3"
              }
          ]
      },
      {
          "id": "text2",
          "text": "text2",
          "type": "text",
          "item": [
              {
                  "id": "text2-1",
                  "text": "text2-1",
                  "type": "text",
                  "item": [
                      {
                          "id": "text2-1-1",
                          "text": "text2-1-1",
                          "type": "text",
                          "item": [
                              {
                                  "id": "text2-1-1-1",
                                  "text": "text2-1-1-1",
                                  "type": "text",
                                  "item": [
                                      {
                                          "id": "text2-1-1-1-1",
                                          "text": "text2-1-1-1-1",
                                          "type": "text"
                                      }
                                  ]

                              }
                          ]
                      }
                  ]
              }
          ]
      }
  ];

参考させていただいたコード

Angular 6 dynamic from builder with Reactive Forms.

参考というかほぼコピペですがwww

ポイント

入力フォームの設定用JSONと同じ階層のFormGroupを作成

  ngOnInit() {
    // FormControl生成
    let fieldsCtrls:any = this.walkJSON(this.fields, function(item) {
      let fieldsCtrl: any = {};
      if (item.type != 'checkbox') {
        fieldsCtrl = new FormControl(item.value || '', Validators.required)
      } else {
        let opts = {};
        for (let opt of item.options) {
          opts[opt.value] = new FormControl('');
        }
        fieldsCtrl = new FormGroup(opts)
      }
      return fieldsCtrl;
    });

    this.form = new FormGroup(fieldsCtrls);
  }

  walkJSON(data, callback){
    const formGroup: any = {};
    data.forEach(item => {

      formGroup[item.id] = callback(item);
      if (item.item) {
        formGroup[item.id + '_child'] = new FormGroup(this.walkJSON(item.item, callback));
      }
    });

    return formGroup;
  }

設定項目の中に子を持つ場合があるので、再帰的に処理します。
FormGroupには必ず名前を付けないといけないので、親のIDに「_child」を付けています。(ダサいのでなんとかしたいですが。。。)

テンプレート側で、コンポーネントを再帰的に呼び出す

  template: `
  <ng-container [formGroup]="form">
    <ng-container [ngSwitch]="field.type">
      <div class="child">
        <textbox *ngSwitchCase="'text'" [field]="field" [form]="form"></textbox>
        <checkbox *ngSwitchCase="'checkbox'" [field]="field" [form]="form"></checkbox>
        <radio *ngSwitchCase="'radio'" [field]="field" [form]="form"></radio>
        <div *ngIf="!isValid && isDirty">{{field.text}} is required</div>
      </div>
    </ng-container>
    <!-- 子を持つ場合 -->
    <ng-container  *ngIf="field.item">
    <div *ngFor="let childField of field.item" class="parent">
        <field-builder [field]="childField" [form]="form.controls[field.id + '_child']"></field-builder>
    </div>
    </ng-container>
  </ng-container>
  `

field-builderコンポーネントから、更にfield-builderコンポーネントを呼び出します。
その際に、同じ階層のFromGroupを指定します。

感想

Angularは、設定ファイルから画面を生成するみたいなことにはあんまり向いてないんじゃないかと思ったけど、ちゃんと仕組みが用意されていた。
学習コストは高いが、慣れれば色々できそうな気がします。

2
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?