Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 16 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,22 @@ $scope.dataForTheTree =

Attributes of angular treecontrol

- treecontrol: the treeview element.
- element content: the template to evaluate against each node for the node label.
- tree-model : the tree data on the $scope. This can be an array of nodes or a single node.
- options : different options to customize the tree control.
- nodeChildren : the name of the property of each node that holds the node children. Defaults to 'children'.
- dirSelection : are directories (nodes with children) selectable? If not, clicking on the dir label will expand and contact the dir. Defaults to true.
- injectClasses : allows to inject additional CSS classes into the tree dom
- ul : inject classes into the ul elements
- li : inject classes into the li elements
- iExpanded : inhect classes into the 'i' element for the expanded nodes
- iCollapsed : inhect classes into the 'i' element for the collapsed nodes
- iLeaf : inhect classes into the 'i' element for leaf nodes
- label : inhect classes into the div element around the label
- on-selection : function to call on the current $scope on node selection.
- selected-node : parameter on the $scope to update with the current selection.
- `treecontrol` : the treeview element.
- element content : the template to evaluate against each node for the node label.
- `tree-model` : the tree data on the `$scope`. This can be an array of nodes or a single node.
- `options` : different options to customize the tree control.
- `nodeChildren` : the name of the property of each node that holds the node children. Defaults to 'children'.
- `dirSelection` : are directories (nodes with children) selectable? If not, clicking on the dir label will expand and contact the dir. Defaults to `true`.
- `equality` : the function used to determine equality between old nodes and new ones when checking whether a replacement node should be expanded and/or marked as selected. Defaults to a function which uses `angular.equals()` on everything except the property indicated in `nodeChildren`.
- `injectClasses` : allows to inject additional CSS classes into the tree DOM
- `ul` : inject classes into the ul elements
- `li` : inject classes into the li elements
- `iExpanded` : inhect classes into the 'i' element for the expanded nodes
- `iCollapsed` : inhect classes into the 'i' element for the collapsed nodes
- `iLeaf` : inhect classes into the 'i' element for leaf nodes
- `label` : inhect classes into the div element around the label
- `on-selection` : function to call on the current `$scope` on node selection.
- `selected-node` : parameter on the `$scope` to update with the current selection.


## Styling
Expand Down
38 changes: 27 additions & 11 deletions angular-tree-control.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(function ( angular ) {
'use strict';

angular.module( 'treeControl', [] )
.directive( 'treecontrol', ['$compile', function( $compile ) {
/**
Expand All @@ -17,12 +17,12 @@
else
return "";
}

function ensureDefault(obj, prop, value) {
if (!obj.hasOwnProperty(prop))
obj[prop] = value;
}

return {
restrict: 'E',
require: "treecontrol",
Expand All @@ -45,6 +45,13 @@
ensureDefault($scope.options.injectClasses, "iCollapsed", "");
ensureDefault($scope.options.injectClasses, "iLeaf", "");
ensureDefault($scope.options.injectClasses, "label", "");
ensureDefault($scope.options, "equality", function (a, b) {
if (a === undefined || b === undefined)
return false;
a = angular.copy(a); a[$scope.options.nodeChildren] = [];
b = angular.copy(b); b[$scope.options.nodeChildren] = [];
return angular.equals(a, b);
});

$scope.expandedNodes = {};

Expand All @@ -65,17 +72,17 @@
};

$scope.nodeExpanded = function() {
return $scope.expandedNodes[this.$id];
return !!$scope.expandedNodes[this.$id];
};

$scope.selectNodeHead = function() {
$scope.expandedNodes[this.$id] = !$scope.expandedNodes[this.$id];
$scope.expandedNodes[this.$id] = ($scope.expandedNodes[this.$id] === undefined ? this.node : undefined);
};

$scope.selectNodeLabel = function( selectedNode ){
if (selectedNode[$scope.options.nodeChildren] && selectedNode[$scope.options.nodeChildren].length > 0 &&
!$scope.options.dirSelectable) {
$scope.expandedNodes[this.$id] = !$scope.expandedNodes[this.$id];
this.selectNodeHead();
}
else {
$scope.selectedScope = this.$id;
Expand Down Expand Up @@ -109,10 +116,14 @@

scope.$watch("treeModel", function updateNodeOnRootScope(newValue) {
if (angular.isArray(newValue)) {
if (angular.isDefined(scope.node) && angular.equals(scope.node[scope.options.nodeChildren], newValue))
return;
scope.node = {};
scope.node[scope.options.nodeChildren] = newValue;
}
else {
if (angular.equals(scope.node, newValue))
return;
scope.node = newValue;
}
});
Expand All @@ -134,7 +145,6 @@
restrict: 'E',
require: "^treecontrol",
link: function( scope, element, attrs, treemodelCntr) {

// Rendering template for the current node
treemodelCntr.template(scope, function(clone) {
element.html('').append(clone);
Expand All @@ -145,6 +155,16 @@
.directive("treeTransclude", function() {
return {
link: function(scope, element, attrs, controller) {
angular.forEach(scope.expandedNodes, function (node, id) {
if (scope.options.equality(node, scope.node)) {
scope.expandedNodes[scope.$id] = scope.node;
scope.expandedNodes[id] = undefined;
}
});
if (scope.options.equality(scope.node, scope.selectedNode)) {
scope.selectNodeLabel(scope.node);
}

scope.$treeTransclude(scope, function(clone) {
element.empty();
element.append(clone);
Expand All @@ -153,7 +173,3 @@
}
});
})( angular );




2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "angular-tree-control",
"version": "0.1.0",
"version": "0.1.2",
"description": "Angular Tree Control",
"main": "angular-tree-control.js",
"scripts": {
Expand Down
49 changes: 49 additions & 0 deletions test/angular-tree-control.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,55 @@ describe('unit testing angular tree control directive', function() {
expect(element.find('.tree-selected').length).toBe(1);
});

it('should retain expansions after full model refresh', function () {
var testTree = createSubTree(2, 2);
$rootScope.treedata = angular.copy(testTree);
element = $compile('<treecontrol tree-model="treedata">{{node.label}}</treecontrol>')($rootScope);
$rootScope.$digest();

element.find('li:eq(0) .tree-branch-head').click();
expect(element.find('li:eq(0)').hasClass('tree-expanded')).toBeTruthy();

$rootScope.treedata = angular.copy(testTree);
$rootScope.$digest();
expect(element.find('li:eq(0)').hasClass('tree-expanded')).toBeTruthy();
});

it('should retain selection after full model refresh', function () {
var testTree = createSubTree(2, 2);
$rootScope.treedata = angular.copy(testTree);
element = $compile('<treecontrol tree-model="treedata">{{node.label}}</treecontrol>')($rootScope);
$rootScope.$digest();

element.find('li:eq(0) div').click();
expect(element.find('.tree-selected').length).toBe(1);

$rootScope.treedata = angular.copy(testTree);
$rootScope.$digest();
expect(element.find('.tree-selected').length).toBe(1);
});

it('should be able to accept alternative equality function', function () {
$rootScope.treedata = createSubTree(2, 2);
$rootScope.treedata[0].id = 'id0';
$rootScope.treeOptions = {equality: function(a, b) {
if (a === undefined || b === undefined || a.id === undefined || b.id === undefined)
return false;
else
return angular.equals(a.id, b.id);
}};
element = $compile('<treecontrol tree-model="treedata" options="treeOptions">{{node.label}}</treecontrol>')($rootScope);
$rootScope.$digest();

element.find('li:eq(0) .tree-branch-head').click();
expect(element.find('li:eq(0)').hasClass('tree-expanded')).toBeTruthy();

$rootScope.treedata = createSubTree(2, 2);
$rootScope.treedata[0].id = 'id0';
$rootScope.$digest();
expect(element.find('li:eq(0)').hasClass('tree-expanded')).toBeTruthy();
});

it('should be able to accept additional class names', function () {
$rootScope.treedata = createSubTree(2, 2);
$rootScope.treeOptions = {injectClasses: {ul: 'ulcls', li: 'licls', iExpanded: 'expandcls',
Expand Down