Reputation: 7819
I have a recursive ng-repeat for creating a branching mind map tree which may have child nodes indefinitely (run the code snippet below to get a better idea of what I mean).
I've got a remove()
function in my $scope
, which intends to remove itself. However, to do this, I need access to the parent. I tried achieving this by using ng-init to set the parent node in a variable called parent
. The problem is, the parent
variable seems to be referring to the node itself instead of its parent. How do I inject the parent into the loop instead?
Everything but the fore-mentioned $scope.remove()
works in the snippet below:
(function(){
angular
.module('createTree',[])
.controller('CreateTreeController', function($scope){
$scope.tree=[{
title:'node 1',
subNodes:[
{
title:'node 1.1',
subNodes:[]
},
{
title:'node 1.2',
subNodes:[
{
title:'node 1.2.1',
subNodes:[]
},
{
title:'node 1.2.2',
subNodes:[]
}
]
},
{
title:'node 1.3',
subNodes:[]
}
]
}];
$scope.addTo=function(node){
node.subNodes.push({
title: node.title+"."+(node.subNodes.length+1),
subNodes:[]
});
}
$scope.remove=function(node,parent){
var index=parent.subNodes.indexOf(node)
if(index>-1) parent.subNodes.splice(index,1);
}
});
})();
*{
position: relative;
box-sizing: border-box;
}
.node{
padding: 1px 0;
}
.node>.subNodes{
margin-left: 10px;
}
.node>.subNodes::before{
content: '';
display: block;
width: 5px;
height: 1px;
background-color: black;
position: absolute;
top: 50%;
left: -10px;
}
.node>.subNodes::after{
content:'';
display: block;
width: 1px;
height: 100%;
background-color: black;
position: absolute;
top: 0;
left: -5px;
z-index: 1;
}
.node>.subNodes>.node::before{
content:'';
display: block;
width: 5px;
height: 1px;
background-color: black;
position: absolute;
top: 50%;
left:-5px;
z-index: 3;
}
.node>.subNodes>.node:first-child::after{
content:'';
display: block;
width: 1px;
height: 50%;
background-color: white;
position: absolute;
top: 0;
left:-5px;
z-index: 2;
}
.node>.subNodes>.node:last-child:not(:first-child)::after{
content:'';
display: block;
width: 1px;
height: calc(50% - 1px);
background-color: white;
position: absolute;
bottom: 0;
left:-5px;
z-index: 2;
}
.node>.subNodes>.node:only-child::after{
height: 100%;
}
.node>*{
display: inline-block;
vertical-align: middle;
}
.node [type=text]{
width: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
<div ng-app="createTree" ng-controller="CreateTreeController as createTree">
<script type="text/ng-template" id="treeNode.html">
<span>
<input
type="button"
value="x"
ng-click="remove(node,parent)"
>
<a href="#">{{node.title}}</a>, parent: {{parent.title}}
<input
type="button"
value="+"
ng-click="addTo(node)"
>
</span><div class="subNodes" ng-show="node.subNodes.length>0">
<div
class="node"
ng-init="parent=node"
ng-repeat="node in node.subNodes"
ng-include="'treeNode.html'"
></div>
</div>
</script>
<div
class="node"
ng-init="parent=tree"
ng-repeat="node in tree"
ng-include="'treeNode.html'"
></div>
</div>
Upvotes: 4
Views: 294
Reputation: 126
The parent can't be the node, You need to set true parent for every node.
I made a solution
(function(){
angular
.module('createTree',[])
.controller('CreateTreeController', function($scope){
$scope.tree=[{
title:'node 1',
parent: null,
subNodes:[]
}];
$scope.addTo=function(node){
node.subNodes.push({
title: node.title+"."+(node.subNodes.length+1),
parent: node,
subNodes:[]
});
}
$scope.remove=function(node,parent){
var index=node.parent.subNodes.indexOf(node)
if(index>-1 && node.parent != null) node.parent.subNodes.splice(index,1);
}
});
})();
*{
position: relative;
box-sizing: border-box;
}
.node{
padding: 1px 0;
}
.node>.subNodes{
margin-left: 10px;
}
.node>.subNodes::before{
content: '';
display: block;
width: 5px;
height: 1px;
background-color: black;
position: absolute;
top: 50%;
left: -10px;
}
.node>.subNodes::after{
content:'';
display: block;
width: 1px;
height: 100%;
background-color: black;
position: absolute;
top: 0;
left: -5px;
z-index: 1;
}
.node>.subNodes>.node::before{
content:'';
display: block;
width: 5px;
height: 1px;
background-color: black;
position: absolute;
top: 50%;
left:-5px;
z-index: 3;
}
.node>.subNodes>.node:first-child::after{
content:'';
display: block;
width: 1px;
height: 50%;
background-color: white;
position: absolute;
top: 0;
left:-5px;
z-index: 2;
}
.node>.subNodes>.node:last-child:not(:first-child)::after{
content:'';
display: block;
width: 1px;
height: calc(50% - 1px);
background-color: white;
position: absolute;
bottom: 0;
left:-5px;
z-index: 2;
}
.node>.subNodes>.node:only-child::after{
height: 100%;
}
.node>*{
display: inline-block;
vertical-align: middle;
}
.node [type=text]{
width: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
<div ng-app="createTree" ng-controller="CreateTreeController as createTree">
<script type="text/ng-template" id="treeNode.html">
<span>
<input
type="button"
value="x"
ng-click="remove(node)"
>
<a href="#">{{node.title}}</a>, parent: {{node.parent.title}}
<input
type="button"
value="+"
ng-click="addTo(node)"
>
</span><div class="subNodes" ng-show="node.subNodes.length>0">
<div
class="node"
ng-repeat="node in node.subNodes"
ng-include="'treeNode.html'"
></div>
</div>
</script>
<div
class="node"
ng-repeat="node in tree"
ng-include="'treeNode.html'"
></div>
</div>
Upvotes: 2
Reputation: 3746
There is an issue with the way you are updating your parent in every loop. Basically what is happening is that after the first level you parent's value is becoming same as that of node. This is actually an angular feature where the ng-init gets called again to update the value of parent because it sees that the value of node has changes in the ng-repeat. So the end result that the value of parent will always be the last element in node.subNodes. Hence when you call remove the parent of the node is not really being passed. To overcome this I have added a third variable which will hold the value of the parent (or rather the next parent). I have created a fiddler here.
I have only changed your HTML as below (the ng-init expression has been changed):
<div ng-controller="CreateTreeController">
<script type="text/ng-template" id="treeNode.html">
<span>
<input
type="button"
value="x"
ng-click="remove(node,parent)"
>
<a href="#">{{node.title}}</a>, parent: {{parent.title}}
<input
type="button"
value="+"
ng-click="addTo(node)"
>
</span>
<div class="subNodes" ng-show="node.subNodes.length>0">
<div class="node" ng-init="parent=oldParent;oldParent=node;" ng-repeat="node in node.subNodes" ng-include="'treeNode.html'"></div>
</div>
</script>
<div class="node" ng-init="parent=tree;oldParent=parent[0];" ng-repeat="node in tree" ng-include="'treeNode.html'"></div>
</div>
Upvotes: 3