使用嵌套对象时,如何在AngularJS中创建递归模板?


71

我正在尝试从JSON对象动态构建表单,该对象包含表单元素的嵌套组:

  $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"]}
    ]},
  ];

我一直在使用ng-switch块,但是它对于嵌套项变得站不住脚,就像上面的Address对象一样。

这是小提琴:http : //jsfiddle.net/hairgamiMaster/dZ4Rg/

关于如何最好地解决此嵌套问题的任何想法?非常感谢!


:我刚才已经回答这一个非常通用的方式在不同的问题上stackoverflow.com/questions/14430655/...
tilgovi

Answers:


122

我认为这可以为您提供帮助。这是我在Google网上论坛上找到的关于树中递归元素的答案。

该建议来自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>

提议的解决方案是关于使用一个模板,该模板使用ng-include指令在当前元素具有子元素时调用自身。

在您的情况下,我将尝试使用ng-switch伪指令创建模板(每种标签类型都使用一种情况,就像您一样),如果有任何子标签,则在末尾添加ng-include。


2
非常感谢-我认为这是使用的技术。干杯!
Hairgami_Master 2013年

如果您的数据结构不重复,因此您不能使用重复器,但是嵌套值不确定,该怎么办?请参阅:stackoverflow.com/questions/25044253/...
Randyaa

@jpmorin:我要如何处理这种情况?您可以在代码查看网站
el.severo,2014年

这个解决方案很棒,但请牢记性能。该解决方案存在严重的性能问题。阅读Ben Nadels文章以获取更多信息:bennadel.com/blog/…–
Samuel Kupferschmid

我可以track by在此解决方案中使用吗?我需要添加键盘导航,并通过其ID搜索每个元素似乎不是一个好的解决方案。
owczarek's

10

结合@jpmorin和@Ketan的建议(@jpmorin的答案略有更改,因为它实际上不能按原样工作)...ng-if可以防止“叶子的孩子”生成不必要的ng-repeat指令:

<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>

这是Plunker的工作版本


1

可能考虑使用ng-switch检查Fields属性的可用性。如果是这样,请对该条件使用其他模板。该模板在Fields数组上具有ng-repeat。


1

我知道这是一个古老的问题,但是对于可能通过搜索来到这里的其他人,尽管我会留下一个解决方案,但对我来说却更简洁。

它基于相同的想法,但是不必将模板存储在模板缓存等中。我希望有一个更“干净”的解决方案,所以我最终创建了https://github.com/dotJEM/angular-tree

使用起来非常简单:

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

由于该指令使用转载而不是编译(从最新版本开始),因此它的性能应比ng-include示例更好。

基于此处数据的示例:

angular
  .module('demo', ['dotjem.angular.tree'])
  .controller('AppController', function($window) {

    this.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"]
      }]
    }, ];

    this.addNode = function(parent) {
      var name = $window.prompt("Node name: ", "node name here");
      parent.children = parent.children || [];
      parent.children.push({
        name: name
      });
    }

    this.removeNode = function(parent, child) {
      var index = parent.children.indexOf(child);
      if (index > -1) {
        parent.children.splice(index, 1);
      }
    }

  });
<div ng-app="demo" ng-controller="AppController as app">

   <form>
        <ul class="unstyled" dx-start-with="app.formData" >
            <li ng-repeat="field in $dxPrior" data-ng-switch on="field.type">
                <div data-ng-switch-when="text">
                    <label>{{field.label}}</label>
                    <input type="text"/>
                </div>
                <div data-ng-switch-when="dropdown">
                    <label>{{field.label}}</label>
                    <select>
                        <option ng-repeat="option in field.options" value="{{option}}">{{option}}</option>
                    </select>
                </div>
                <div data-ng-switch-when="group" class="well">
                    <h2>{{field.label}}</h2>
                    <ul class="unstyled" dx-connect="field.Fields" />
                </div>   
            </li>
        </ul>
            <input class="btn-primary" type="submit" value="Submit"/>
    </form>
  
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
  <script src="https://rawgit.com/dotJEM/angular-tree-bower/master/dotjem-angular-tree.min.js"></script>

</div>


0

我只想在基于属性的结构的情况下扩展jpmorin post。

JSON

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

如您所见,json对象此处不包含数组结构。

的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>

在这种情况下,您可以对其进行迭代。

有关ng-repeat over属性的角度文档,在这里

此处可以找到一些行实现。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.