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