JSFiddle - React, Tailwind, and code Playground

by maxisam

HTML

<!doctype html>
<html>
<head>
  <title>dynamic form</title>
  <script src="http://code.angularjs.org/angular-0.9.14.js" ng:autobind></script>
</head>
<body ng:controller="DynFormCtrl">
  <my:form ng:repeat="address in addresses" schema="addressSchema" data="address"></my:form>
  <div id="model">
    addresses = <pre>{{addresses}}</pre>
  </div>
</body>
</html>

CSS

fieldset {
    padding-bottom: 2em;
}

#model {
    position: fixed;
    right: 0;
    top: 0;
}

JavaScript

function DynFormCtrl() {
      // define data we are working with
      this.addresses = [{
        name: 'John Smith',
        friend: true,
        address:{line1: '123 Main St.', city:'Anytown', state:'AA', zip:'12345'},
        contacts:{type:'phone', value:'1(234) 555-1212'},
        notes: 'snort laughs often'
      }, {
        name: 'Bobo Boom',
        address:{line1: '3 Hide St.', city:'Heretown', state:'CA', zip:'12345'},
        contacts:{type:'phone', value:'1(234) 555-1212'}
      }];

      // define schema for the data (each attribute except for 'children' translates to 
      // DOM element attribute of the same name).
      // if 'type' is undefined, it is considered to be 'text'
      this.addressSchema = [
        { name: 'name'},
        { name: 'friend', type: 'checkbox'},
        { name: 'address',
          children: [
            { name: 'line1' },
            { name: 'city' },
            { name: 'state', size: 2, 'ng:validate': 'regexp:validatorRegExps.state' },
            { name: 'zip', size: 5, 'ng:validate': 'number' }
          ]
        },
        { name: 'contacts',
          children: [
            { name: 'type' },
            { name: 'value' }
          ]
        },
        { name: 'notes', type: 'textarea'}
      ]
      
      this.validatorRegExps = {
        state: /[A-Z]{2}/
      }
    }

    angular.widget('my:form', function(element) {
      this.directives(true);
      return function(element) {
        var scope = this,
            schema = scope.$eval(element.attr('schema')),
            model = element.attr('data'),
            fieldset = angular.element('<fieldset></fieldset>');

        angular.forEach(schema, function processField(field) {
          var name = this.model + '.' + field.name,
              fieldElStr;
          
          if (field.children) {
            angular.forEach(field.children, processField, {model: name});
            return;
          }

          switch (field.type || 'text') {
       ...