Angular (semi) dynamic form generation

I'm trying to simplify how I generate my forms with AngularJS.

My ultimate goal is to be able to write something like:

<form>
  <field-div label="Name">
    <field which="form.name"></field>
  </field-div>

  <field-div label="Language">
    <field which="form.language"></field>
  </field-div>
</form>

For this, I'm working with two directives (fieldDiv and field) and two JavaScript objects: one that represents the data being edited in the form, and another that represents the form definition (field type, field options...).

See this JSFIDDLE for the code: http://jsfiddle.net/vincedo/9Uf6C/

After banging my head against the wall a few times, I think I got it working. The main difficulty was to get Angular to treat a string -- that I got from my form definition object (e.g. "entity.name") -- as a scope property with two-way data binding.

I still have two questions:

  • Is it the right way to do it? More specifically, my code uses two scope.$watch per field, and I'm afraid it might affect performance.
  • In my form, why isn't "English" selected in the "Language" field? It must be a stupid mistake: the HTML source uses 0, 1, 2... as <option> values whereas my code uses string keys such as en, fr... But after trying to wrap my head around $wrap and $parse for so many hours, I don't see it. :-)

Thanks!

You can do this with an ng-switch tag (http://docs.angularjs.org/api/ng.directive:ngSwitch). Something like this.

<div class="control-group" ng-repeat="element in form">
  <label class="control-label">{{element.label}}</label>
  <div class="controls" ng-switch="element.widget">
    <input ng-switch-when="text" type="text" ng-model="entity[element.model]" ng-required="element.required" />
    <select ng-switch-when="select" ng-model="entity[element.model]" ng-required="element.required" ng-options="o.key as o.name for o in element.options"></select>
  </div>
</div>

Here is a working example of my solution :

http://jsfiddle.net/Galdo/Sqsc7/17/

Please note that I changed entity.name to name in your schema.

With this solution, you will avoid any use of a directive. Pure angular!

For your second question (why the 0, 1, 2, ... instead of 'en', 'fr', etc.), you'll need to use an object instead of an array to bind to the <select> element. I found the answer in another question, but in short, your model becomes { 'en': 'English', 'fr': 'French', 'und':'Undefined'}, and your template's ng-options becomes ng-options="k as v for (k,v) in options"

I updated your Fiddle here: http://jsfiddle.net/StevenLJackson1/9Uf6C/7/

I'm also interested in performance with the multiple watches (i.e., the answer to your first question), since I am doing something similar on my current project.

I created a simple form builder application, source code: https://github.com/Selmanh/angularjs-form-builder

You can see it online here: http://selmanh.github.io/angularjs-form-builder/

My solution was similar to yours, but didn't use watch.

You might also consider looking at this, which creates dynamic angular forms based on simple json definitions:

https://github.com/danhunsaker/angular-dynamic-forms