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

人盡茶涼 提交于 2020-01-17 11:34:13

问题


Angularjs: complex directive in ng-repeat, how to bind the ngModel or ngChecked in directive and make it work? given the array:

+----+-----------+----------+----------+-------+
| 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}
];

so in html

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

and the directive

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);
    }
});

So how to bind the model value in the directive? here is the plunker http://plnkr.co/edit/QYqfEVaQF8WmUvB1aHB6?p=preview

additional info:

the haschild represent the the parent has child element

for example:

1.people A own multiple company, so the people A has child

2.then one of the company has multiple employee, then that company has child. so in my directive, if this element(assume this is element A) has child then generate the child element and input and bind to the ngModel.

My Answer: this is how i think and simple way to achieve 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>

however, i think @NewDev's answer is the correct answer!


回答1:


Basically, what you are looking for is to create an arbitrary hierarchical DOM structure and bind it to some equal hierarchical data structure (or ViewModel).

How you represent your hierarchy - I'll leave it up to you. It's not essential for the question.

For simplicity, suppose we model the hierarchy as a tree structure:

$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:[]}
            ]}
          ]}
        ]};

You then need to traverse this tree and create DOM elements bound to some object o (bear with me):

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

Then, this needs to be compiled against a scope. But since the structure is arbitrary, you could create a new child scope for each node.

So, what is o? o will be the object we will create on each child scope created for each node of the tree.

So, suppose you have a recursive function traverseTree, that 1) creates the DOM element, 2) compiles against this scope, and 3) creates a child scope for each child. It could look like something as follows:

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);
  }
}

The directive's link function kicks off the tree traversal:

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);
    }
  };

});

The usage is:

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

Here's a plunker




回答2:


I am sorry I just saw so here is my 2 pence for everyone's benefit.

Assuming

$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:[]}
        ]}
      ]}
    ]};

We can do

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

Sorry have not tested this snippet did not have time, but this is the way it can work without coding on a hierarchal structure. Have used it many times.



来源:https://stackoverflow.com/questions/28058702/angularjs-how-to-create-dom-and-bind-to-a-model-based-on-arbitrary-hierarchica

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!