I have a treeview directive credit to http://codepen.io/bachly/pen/KwWrzG for being my starting block. that I am trying to update when I add objects to the collection. I can update the object and insert the new objects but the treeview directive is never called once the $scoped
item is updated.
Ultimately the data used will come from a service at this point I am just testing with mock data.
The original collection looks like this
$scope.myList = {
children: [
{
name: "Event",
children: [
{
name: "Event Date",
parent:"Event",
children: [
{
name: "2008",
filterType: '_eventStartDate',
parent: 'Event'
},
{
name: "2009",
filterType: '_eventStartDate',
parent: 'Event'
}
]
},
{
name: "Event Attendee",
parent: "Event",
children: [
{
name: "Person 1",
filterType: '_eventAttenddeeName',
parent: 'Event Attendee'
},
{
name: "Person 2",
filterType: '_eventAttenddeeName',
parent: 'Event Attendee'
}
]
}
]
}]
};
var TheOtherCollection = {
children: [
{
name: "A New Event",
children: [
{
name: "The Other Date",
parent: " A New Event",
children: [
{
name: "2010",
FilterType: '_eventStartDate',
Parent: '_event'
},
{
name: "2011",
FilterType: '_eventStartDate',
Parent: '_event'
}
]
}
]
}]
};
This generates a tree view with checkboxes using the following directive and html
app.directive('tree', function () {
return {
restrict: 'E',
replace: true,
scope: {
t: '=src',
filter: '&'
},
controller: 'treeController',
template: '<ul><branch ng-repeat="c in t.children track by $index" src="c" filter="doSomething(object, isSelected)"></branch></ul>'
};
});
app.directive('branch', function($compile) {
return {
restrict: 'E',
replace: true,
scope: {
b: '=src',
filter: '&',
checked: '=ngModel'
},
template: '<li><input type="checkbox" ng-click="innerCall()" ng-model="b.$$hashKey" ng-change="stateChanged(b.$$hashKey)" ng-hide="visible" /><a>{{ b.name }}</a></li>',
link: function (scope, element, attrs) {
var clicked = '';
var hasChildren = angular.isArray(scope.b.children);
scope.visible = hasChildren;
if (hasChildren) {
element.append('<tree src="b"></tree>');
$compile(element.contents())(scope);
}
element.on('click', function(event) {
event.stopPropagation();
if (hasChildren) {
element.toggleClass('collapsed');
}
});
scope.stateChanged = function(b) {
clicked = b;
};
scope.innerCall = function() {
scope.filter({ object: scope.b, isSelected: clicked });
};
}
};
});
And then the html
<div ng-controller="treeController">
<tree src="myList" iobj="object" filter="doSomething(object, isSelected)"></tree>
<a ng-click="clicked()"> link</a>
</div>
When a checkbox is clicked the new collection is added to the existing one using lodashjs
ng-click event
$scope.doSomething = function (object, isSelected) {
if (isSelected) {
var item = object;
console.log(item);
nestAssociation(object, $scope.myList, TheOtherCollection);
}
}
which creates the new array and adds it within the children array
function nestAssociation(node, oldCollection, newAggregates) {
// var item = fn(oldCollection, node.parent);
var updatedArray = _.concat(oldCollection.children, newAggregates);
console.log(updatedArray);
if (updatedArray != null)
updateMyList(updatedArray);
}
I can see in the output I have a new object but I can't get the treeview to update. I have tried within the directive to add a $compile(element) on the click event in the directive but since the array is not built yet nothing changes.
Do I need to add a $watch to this directive and if so where or is there some other way I can get the directive to re-render and display the new nested collection?
Update
Base on some of the feedback and questions here is a little more detail around the question. The issue I am seeing is not in the directive as far as moving data around the issue is I cannot get the treeview to re-render once an array is added to the existing model. The following link is a working plunker that shows the project as it currently works. Running chrome dev tools I can see in the output the model is updated after a checkbox is selected
While I see the object is updated, the directive never updates to show the new array added to the object. This is the part that I need help understanding.
thanks in advance
You pass the function to the inner directives (which is the best practice), but you have access to scope.filter
. Not doSomethingFunction
. This one is undefined
there.
filter="doSomething(object, isSelected)"
=>filter="filter(object, isSelected)"
app.directive('tree', function () {
return {
restrict: 'E',
replace: true,
scope: {
t: '=src',
filter: '&'
},
controller: 'treeController',
template: '<ul>
<branch ng-repeat="c in t.children track by $index"
src="c" filter="filter(object, isSelected)">
</branch>
</ul>'
};
});
Next :
You can never access $$
variables in angularJS, because they are private. Maybe you should make one from your DB..., but the $$hashkey
seems a easy solution though.
checked
attribute might throw an error, because ngModel
does not exist on your tree directive template. (put at least a ?
before)
A checkbox can not have as model a $$hashkey.
Ng-change and ng-click will always be called at the same time, use the simplest one.
app.directive('branch', function($compile) {
return {
restrict: 'E',
replace: true,
scope: {
b: '=src',
filter: '&'
},
template: '<li><input type="checkbox" ng-click="innerCall(b.$$hashKey)" ng-model="isChecked" ng-hide="visible" /><a>{{ b.name }}</a></li>',
link: function (scope, element, attrs) {
scope.isChecked = false;
var hasChildren = angular.isArray(scope.b.children);
scope.visible = hasChildren;
if (hasChildren) {
element.append('<tree src="b"></tree>');
$compile(element.contents())(scope);
}
element.on('click', function(event) {
event.stopPropagation();
if (hasChildren) {
element.toggleClass('collapsed');
}
});
scope.innerCall = function(hash) {
if(scope.isChecked){
scope.filter({ object: scope.b, isSelected: hash });
}
};
}
};
});
UPDATE
You have the same treeController
in your tree
directive and in your index.html
view.
This is what causes the view not to update!
I deleted the one in your directive, otherwise you'll have a controller for each child.
You saw the good console.log message in your controller, but it was in a controller for ONE directive.
You were not accessing the controller of the index.html
.
Then I fixed the filter
function communication between childs :
You forgot to communicate the filter function when you append new tree
's :
element.append('<tree src="b" filter="filter({ object: object, isSelected: isSelected })"></tree>');
Also, in your parent directive template, you also need the hash to send parameters to the function :filter="filter({ object: object, isSelected: isSelected })"
I edited your Plunker HERE without changing the code with the above comments I made.
(I'm not sure what you write is not what you want and because you did not comment I rather not change it so you still undertand your code fast)
But the view is updating now!
I think a little debug with what you want and the comments above should be enough.
EDIT 2
You forgot to return an object with the property chrilden
. You returned an array, which caused the problem.
function updateMyList(data) {
var transformed = { children : data };
$scope.myList = transformed;
}
Here is a working PLUNKER.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With