How can I make recursive templates in AngularJS when using nested objects?

I'm trying to build a form dynamically from a JSON ojbect, which contains nested groups of form elements:

  $scope.formData = [
  {label:'First Name', type:'text', required:'true'},
  {label:'Last Name', type:'text', required:'true'},
  {label:'Coffee Preference', type:'dropdown', options: ["HiTest", "Dunkin", "Decaf"]},
  {label: 'Address', type:'group', "Fields":[
      {label:'Street1', type:'text', required:'true'},
      {label:'Street2', type:'text', required:'true'},
      {label:'State', type:'dropdown',  options: ["California", "New York", "Florida"]}
    ]},
  ];

I've been using ng-switch blocks, but it becomes untenable with nested items, like in the Address object above.

Here's the fiddle: http://jsfiddle.net/hairgamiMaster/dZ4Rg/

Any ideas on how to best approach this nested problem? Many thanks!

I think that this could help you. It is from an answer I found on a Google Group about recursive elements in a tree.

The suggestion is from Brendan Owen: http://jsfiddle.net/brendanowen/uXbn6/8/

<script type="text/ng-template" id="field_renderer.html">
    {{data.label}}
    <ul>
        <li ng-repeat="field in data.fields" ng-include="'field_renderer.html'"></li>
    </ul>
</script>

<ul ng-controller="NestedFormCtrl">
    <li ng-repeat="field in formData" ng-include="'field_renderer.html'"></li>
</ul>

The proposed solution is about using a template that uses the ng-include directive to call itself if the current element has children.

In your case, I would try to create a template with the ng-switch directive (one case per type of label like you did) and add the ng-include at the end if there are any child labels.

Combining what @jpmorin and @Ketan suggested (slight change on @jpmorin's answer since it doesn't actually work as is)...there's an ng-if to prevent "leaf children" from generating unnecessary ng-repeat directives:

<script type="text/ng-template" id="field_renderer.html">
  {{field.label}}
  <ul ng-if="field.Fields">
      <li ng-repeat="field in field.Fields" 
         ng-include="'field_renderer.html'">
      </li>
  </ul>
</script>
<ul>
  <li ng-repeat="field in formData" ng-include="'field_renderer.html'"></li>
</ul>

here's the working version in Plunker

Might consider using ng-switch to check availability of Fields property. If so, then use a different template for that condition. This template would have an ng-repeat on the Fields array.

I know this is an old question, but for others who might come by here though a search, I though I would leave a solution that to me is somewhat more neat.

It builds on the same idea, but rather than having to store a template inside the template cache etc. I wished for a more "clean" solution, so I ended up creating https://github.com/dotJEM/angular-tree

It's fairly simple to use:

<ul dx-start-with="rootNode">
  <li ng-repeat="node in $dxPrior.nodes">
    {{ node.name }}
    <ul dx-connect="node"/>
  </li>
</ul>

Just want to extend jpmorin post in case of property based structure:

 JSON:
{
          "id": 203,
          "question_text_id": 1,
          "yes": {
            "question_text_id": 25,
            "yes": {
              "question_text_id": 26
            }
          },
          "no": {
            "question_text_id": 4
          }
        }

As you can see json object here doesn't contain array structure.

HTML

<div>
    <script type="text/ng-template" id="tree_item_renderer.html">
        <span>{{key}} {{value}}</span>
        <ul>
            <li ng-repeat="(key,value) in value.yes" ng-include="'tree_item_renderer.html'"></li>
            <li ng-repeat="(key,value) in value.no" ng-include="'tree_item_renderer.html'"></li>
        </ul>
    </script>

    <ul>
        <li ng-repeat="(key,value) in symptomeItems" ng-include="'tree_item_renderer.html'"></li>
    </ul>
</div>

However you can iterate over it.

Angular documentation for ng-repeat over properties here

And some row implementation here

The solution from Brendan Owen has following issue for more than 10 levels of nesting:

10 $digest() iterations reached. Aborting!