title: Creating dynamic forms in angular2 using metadata
body: <p>I am trying to create CRUD forms using metadata. I have created is a very basic version of a custom component which creates a form from model. However the custom form at this moment does not update the parent model. </p>
  
  <p>How do I approach the flow of changes from the great-control(see code) to the parent? I think what I need is some functionality like ng-model directive. It would be nice to have the solution extensible to allow for validation.</p>
  
  <p><strong>Basic version of dynamic form:</strong> <a href="http://plnkr.co/edit/BW6hluJ0PZG5GF5gsO6G?p=preview" rel="nofollow">http://plnkr.co/edit/BW6hluJ0PZG5GF5gsO6G?p=preview</a> </p>
  
  <pre><code>import {Component, View, bootstrap, CORE_DIRECTIVES} from 'angular2/angular2';
  import {FORM_DIRECTIVES, FORM_BINDINGS, NgFormModel, ControlGroup, Control, Validators} from 'angular2/forms';
  
  
  @Component({selector: 'great-control', properties: ['column', 'value: data']})
  @View({
    template: `
    &lt;template [ng-if]="column.visible"&gt;
        &lt;div class="pure-control-group"&gt;
          &lt;span [ng-switch]="htmlElementType"&gt;
             &lt;template [ng-switch-when]="'input'"&gt;
               &lt;label [attr.for]="column.name"&gt;{{column.display}}&lt;/label&gt;
               &lt;input id="column.name" [attr.type]="computeInputSubType()" [(ng-model)]="value" 
                  [attr.placeholder]="column.display"&gt; {{value}}
             &lt;/template&gt;
             &lt;template [ng-switch-when]="'checkbox'"&gt;
               &lt;div class="pure-controls"&gt;
                &lt;label for="column.name" class="pure-checkbox"&gt;
                    &lt;input id="column.name" type="checkbox" [(ng-model)]="value"&gt; Toggle and see the change {{value}}
                &lt;/label&gt;
              &lt;/div&gt;
             &lt;/template&gt;
             &lt;template [ng-switch-when]="'option'"&gt;
                &lt;label [attr.for]="column.name"&gt;{{column.display}}&lt;/label&gt;
                &lt;select [(ng-model)]="value"&gt;
                    &lt;option *ng-for="#key of column.values" [value]="key"&gt;{{key}}&lt;/option&gt;
                &lt;/select&gt;{{value}}
              &lt;/div&gt;
             &lt;/template&gt;
             &lt;template [ng-switch-default]"&gt;
                     &lt;span&gt;Oops! This control type is unknown. Contact the creator&lt;/span&gt;
              &lt;/template&gt;
          &lt;/span&gt;
  
  
        &lt;/div&gt;
    &lt;/template&gt;
    &lt;template [ng-if]="!column.visible"&gt;
        There is a hidden control here because the column is set to invisible 
        &lt;input type="hidden" [(ng-model)]="value" /&gt;
    &lt;/template&gt;
  
  
  
    `,
    directives: [FORM_DIRECTIVES,CORE_DIRECTIVES]
  })
  class GreatControl {
    column: Object;
    value: Object;
    htmlElementType:string;
    constructor() { 
    }
  
    onInit(){
      this.htmlElementType = this.computeHtmlElementType();
    }
  
    computeHtmlElementType(): string {
      if (this.column.type == "boolean") {
        return "checkbox";
      } else if (this.column.type == "enum") {
        return "option";
      } else if (this.column.type == "text" || this.column.type == "email" || this.column.type == "number"){
        return "input"
      }else{
        return "unknown"
      }
    }
  
    computeInputSubType(){
      if(this.column.type == "text"){
        return "text";
      } else if(this.column.type == "email"){
        return "email";
      } else if( this.column.type == "number"){
        return "number";
      } else {
        return "text";
      }
    }
  }
  
  
  @Component({
    selector: 'app',
    bindings: [FORM_BINDINGS]
  })
  @View({
    template: `
      &lt;div class="pure-g"&gt;&lt;div class="pure-u-1-1"&gt;
        &lt;form [ng-form-model]='form' class="pure-form pure-form-aligned"&gt;
          &lt;fieldset&gt;
            &lt;great-control *ng-for="#t of columns" [column]="t" [data]="data[t.name]" &gt;
            &lt;/great-control&gt;
              &lt;div class="pure-controls"&gt;
                  &lt;button type="submit" class="pure-button pure-button-primary"&gt;Submit&lt;/button&gt;
              &lt;/div&gt;
          &lt;/fieldset&gt;
        &lt;/form&gt;
      &lt;/div&gt;&lt;div class="pure-u-1-1"&gt;&lt;p&gt;
        As of now this does not change. We need it to change
      &lt;br&gt;&lt;pre&gt;{{dataString()}}&lt;/pre&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;`,
    directives: [FORM_DIRECTIVES, CORE_DIRECTIVES, GreatControl]
  })
  class App {
    data: Object ;
    columns = [
        {name: "column1", display:"This is number only", visible:true, type: "number", length:"10"},
        {name: "column2", display:"This a text field", visible:true, type: "text", length:"10"},
        {name: "column3", display:"Column 3", visible:false, type: "text", length:"10"},
        {name: "column4", display:"Toggle and see", visible:true, type: "boolean"},
        {name: "column5", display:"Column 5", visible:true, type: "enum", values:[ "Blue", "Yellow", "White"]}
      ];
    constructor() {
        this.data= { column1 : "10" , column3: "Not a secret",column4: false, column5: "Yellow" };
      }
    onSubmit(f) {
      console.log(this.data);
    }
    dataString(){
      return JSON.stringify(this.data, null, 2);
    }
    valueOf(obj) {
        if (obj !== undefined &amp;&amp; obj !== null) return obj;
        else return "";
    }
  }
  
  
  bootstrap(App);
  </code></pre>
  
  <p><strong>Expected behaviour(not dynamic):</strong> <a href="http://plnkr.co/edit/kQ0sMT4jItvj3e5uLHtD?p=preview" rel="nofollow">http://plnkr.co/edit/kQ0sMT4jItvj3e5uLHtD?p=preview</a> </p>
  
  <pre><code>import {Component, View, bootstrap, NgIf, CORE_DIRECTIVES} from 'angular2/angular2';
  import {FORM_DIRECTIVES, FORM_BINDINGS, NgFormModel, ControlGroup, Control, Validators} from 'angular2/forms';
  
  @Component({
    selector: 'app',
    bindings: [FORM_BINDINGS]
  })
  @View({
    template: `
      &lt;div class="pure-g"&gt;&lt;div class="pure-u-1-1"&gt;
        &lt;form [ng-form-model]='form' class="pure-form pure-form-aligned"&gt;
          &lt;fieldset&gt;
              &lt;div class="pure-control-group"&gt;
                  &lt;label [attr.for]="columns[0].name"&gt;{{columns[0].display}}&lt;/label&gt;
                  &lt;input id="columns[0].name" type="number" [(ng-model)]="data[columns[0].name]" [attr.placeholder]="columns[0].display"&gt;
              &lt;/div&gt;
              &lt;div class="pure-control-group"&gt;
                  &lt;label [attr.for]="columns[1].name"&gt;{{columns[1].display}}&lt;/label&gt;
                  &lt;input id="columns[1].name" type="text" [(ng-model)]="data[columns[1].name]" [attr.placeholder]="columns[1].display"&gt;
              &lt;/div&gt;
              &lt;div class="pure-control-group"&gt;
                  There is a hidden control here because the column is set to invisible
                  &lt;input type="hidden" [(ng-model)]="data[columns[2].name]" /&gt;
              &lt;/div&gt;
              &lt;div class="pure-controls"&gt;
                &lt;label for="columns[3].name" class="pure-checkbox"&gt;
                    &lt;input id="columns[3].name" type="checkbox" [(ng-model)]="data[columns[3].name]"&gt; Toggle and see the change
                &lt;/label&gt;
              &lt;/div&gt;
              &lt;div class="pure-control-group"&gt;
                  &lt;label [attr.for]="columns[4].name"&gt;{{columns[4].display}}&lt;/label&gt;
                  &lt;select [(ng-model)]="data[columns[4].name]"&gt;
                      &lt;option *ng-for="#state of columns[4].values" [value]="state"&gt;{{state}}&lt;/option&gt;
                  &lt;/select&gt;
              &lt;/div&gt;
              &lt;div class="pure-controls"&gt;
                  &lt;button type="submit" class="pure-button pure-button-primary"&gt;Submit&lt;/button&gt;
              &lt;/div&gt;
          &lt;/fieldset&gt;
        &lt;/form&gt;
      &lt;/div&gt;&lt;div class="pure-u-1-1"&gt;&lt;p&gt;&lt;pre&gt;{{dataString()}}&lt;/pre&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;`,
    directives: [FORM_DIRECTIVES, CORE_DIRECTIVES]
  })
  class App {
    data: Object ;
    columns = [
        {name: "column1", display:"This is number only", visible:true, type: "number", length:"10"},
        {name: "column2", display:"This a text field", visible:true, type: "text", length:"10"},
        {name: "column3", display:"Column 3", visible:false, type: "text", length:"10"},
        {name: "column4", display:"Toggle and see", visible:true, type: "boolean"},
        {name: "column5", display:"Column 5", visible:true, type: "enum", values:[ "Blue", "Yellow", "White"]}
      ];
    constructor() {
        this.data= { column1 : "10" , column3: "Not a secret",column4: false, column5: "Yellow" };
      }
    onSubmit(f) {
      console.log(this.data);
    }
    dataString(){
      return JSON.stringify(this.data, null, 2);
    }
    valueOf(obj) {
        if (obj !== undefined &amp;&amp; obj !== null) return obj;
        else return "";
    }
  }
  
  
  bootstrap(App);
  </code></pre>
  
  <p>Any pointers to solve this is much appreciated.
  Thanks for the help in advance.</p>