AngularJS :如何创建 DOM 并绑定到基于任意分层数据的模型

AngularJS : how to create DOM and bind to a model based on arbitrary hierarchical data

Angularjs:ng-repeat 中的复杂指令,如何绑定 ngModel 或 ngChecked in 指令并使其工作? 给定数组:

+----+-----------+----------+----------+-------+
| id |   name    | parentid | haschild | value |
+----+-----------+----------+----------+-------+
|  1 | parent    | null     | false    | true  |
|  2 | id1 child | 1        | true     | true  |
|  3 | id2 child | 2        | true     | true  |
|  4 | id3 child | 3        | false    | true  |
|  5 | id1 child | 1        | true     | true  |
|  6 | id5 child | 5        | false    | true  |
+----+-----------+----------+----------+-------+
$scope.permission = [
   {"id":1,"name":"parent","value":true,"parentid":"","haschild":false}, 
   {"id":2,"name":"id-1 child","value":true,"parentid":1,"haschild":true},
   {"id":3,"name":"id-2 child","value":true,"parentid":2,"haschild":true},
   {"id":4,"name":"id-3 child","value":true,"parentid":3,"haschild":false},
   {"id":5,"name":"id-1 child","value":true,"parentid":1,"haschild":true},
   {"id":6,"name":"id-5 child","value":true,"parentid":5,"haschild":false}
];

所以在html

<div ng-repeat="x in permission" ng-if="x.parentid == undefined"  has-child="{{x}}"></div>

和指令

myApp.directive('hasChild', function($compile) {
    return function(scope, element, attrs) {
        var j = JSON.parse(attrs.hasChild);
        function generatePermissionDom(thisElement,thisParent)
        {
            angular.forEach(scope.permission,function(o,i){
                if(thisParent.id==o.parentid) 
                {
                    var e = $('<div><input type="checkbox" ng-model="o.value"/>'+o.name+'</div>');
                    generatePermissionDom(e,o);
                    $(thisElement).append(e);
                }
            });
        }

        if(j.haschild)
            angular.forEach(scope.permission,function(o,i){
                if(o.parentid==j.id) 
                {   
                    var e = $('<div><input type="checkbox" ng-model="o.value"/>'+o.name+'</div>');
                    if(o.haschild)
                        generatePermissionDom(e,o);
                    $(element).append(e);
                }
            });

        var p = $(element);
        $compile(element.contents())(scope);
    }
});

那么如何在指令中绑定模型值呢? 这是笨蛋 http://plnkr.co/edit/QYqfEVaQF8WmUvB1aHB6?p=preview

补充信息:

有child表示parent有child个元素

例如:

1.people A拥有多家公司,所以A人有child

2.then其中一家公司有多名员工,那么那家公司有child。 所以在我的指令中,如果此元素(假设这是元素 A)具有 child,则生成 child 元素并输入并绑定到 ngModel。

我的回答:这就是我的想法和实现的简单方法 http://plnkr.co/edit/olKb5oBoxv0utlNcBQPg?p=preview

var app = angular.module('plunk', []);
app.directive('generatePermissions', function($compile) {
  return {
    restrict : "E",
    scope : true,
    require : 'ngModel',
    link:function(scope,element,attrs,ngModel)
    {
      function generatePermissionDom(parent,parentElement)
      {

          angular.forEach(scope.list,function(o,i){
              if(parent.id==o.parentid) 
              {
                  var e = angular.element('<div style="border:1px solid red"><input type="checkbox" ng-model="list['+i+'].value"/></div>');
                  if(o.haschild)
                    generatePermissionDom(o,e);
                  parentElement.append(e);
              }
          });
      }
      angular.forEach(scope.list,function(o,i){

        if(o.parentid == null)
        {
          var e = angular.element('<div style="border:1px solid red"><input type="checkbox" ng-model="list['+i+'].value"/></div>');
          if(o.haschild)
            generatePermissionDom(o,e);
          element.append(e);
        }
      });
      var p = $(element);
      $compile(p.contents())(scope);
    }
  }
});


app.controller('MainCtrl', function($scope) {

  $scope.list = [{"id":1,"name":"parent","value":true,"parentid":null,"haschild":true},{"id":2,"name":"id-1 child","value":false,"parentid":1,"haschild":true},{"id":3,"name":"id-2 child","value":false,"parentid":2,"haschild":true},{"id":4,"name":"id-3 child","value":false,"parentid":3,"haschild":false},{"id":5,"name":"id-1 child","value":false,"parentid":1,"haschild":true},{"id":6,"name":"id-5 child","value":false,"parentid":5,"haschild":false}];
  $scope.checkValue = function()
  {
    angular.forEach($scope.list,function(o,i){
      console.log("id:"+o.id+"  name:"+o.name+"  value:"+o.value);
    });
  }
});

html

  <body ng-controller="MainCtrl">
    <generate-permissions ng-model="list"></generate-permissions>
    <button ng-click="checkValue()">check</button>
  </body>

不过,我认为@NewDev的回答是正确答案!

基本上,您正在寻找的是创建一个任意的分层 DOM 结构并将其绑定到某个相同的分层数据结构(或 ViewModel)。

你如何表示你的层次结构 - 我会留给你。这不是问题所必需的。

为简单起见,假设我们将层次结构建模为树结构:

$scope.tree = {n: "root", v: false, 
      c: [
        {n: "L11", v: false, c: []},
        {n: "L12", v: false, c: [
          {n: "L21", v: true, c: [
            {n: "L31", v: false, c:[]}
            ]}
          ]}
        ]};

然后您需要遍历这棵树并创建 DOM 个绑定到某个对象的元素 o(请耐心等待):

<div><input ng-model="o.v">{{o.n}}</div>

然后,这需要针对范围进行编译。但由于结构是任意的,您可以为每个节点创建一个新的子作用域。

那么,什么是oo 将是我们将在为树的每个节点创建的每个子作用域上创建的对象。

因此,假设您有一个递归函数 traverseTree,它 1) 创建 DOM 元素,2) 针对此作用域进行编译,以及 3) 为每个子作用域创建一个子作用域。它可能如下所示:

function traverseTree(n, scope, parent){
  var me = angular.element("<div><input type='checkbox' ng-model='o.v'>{{o.n}}</div>");
  $compile(me)(scope);
  parent.append(me);

  for (var i = 0; i < n.c.length; i++) {
    var c = n.c[i];
    var childScope = scope.$new(true);
    childScope.o = c; // set object "o" on the scope

    traverseTree(c, childScope, me);
  }
}

指令的 link 函数启动树遍历:

app.directive("tree", function($compile){

  function traverseTree(n, scope, parent){
     // as above
  }

  return {
    restrict: "A",
    scope: {
      root: "=tree"
    },
    link: function(scope, element, attrs){
      var childScope = scope.$new(true);
      childScope.o = scope.root;

      traverseTree(scope.root, childScope, element);
    }
  };

});

用法是:

<div tree="tree"></div>

这是一个plunker

很抱歉我刚刚看到所以这是我的 2 便士,为了大家的利益。

假设

$scope.tree = {n: "root", v: false, 
  c: [
    {n: "L11", v: false, c: []},
    {n: "L12", v: false, c: [
      {n: "L21", v: true, c: [
        {n: "L31", v: false, c:[]}
        ]}
      ]}
    ]};

我们可以

<script type="text/ng-template" id="Tree">
    <input type='checkbox' ng-model='o.v'>{{o.n}}
    <p ng-if="o.c">
        <p ng-repeat="o in o.c" ng-include="'Tree'"></p>
    </p>
</script>
<div>
    <p ng-repeat="o in tree" ng-include="'Tree'"></p>
</div>

抱歉没有时间测试此代码段,但这是无需在分层结构上编码即可工作的方式。用过很多次了