Storing checked checkboxes into array in angularjs tree structure - javascript

Currently i am able to display item that have parent child relation in tree structure using checkboxes. Now I need to store the checked checkboxes into one array so that I can submit that data to server via ajax.
I am new to angularjs. I tried printing using ng-model value. But it doesn't work.
Can you help me with how I can store the checked checkboxes into array.
Below is the code.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script>
var app, list;
list = [
{
name: 'Developer',
opened: true,
children: [
{
name: 'Front-End',id:1,
children: [
{
name: 'Jack',id:2,
title: 'Leader'
},
{
name: 'John',id:3,
title: 'Senior F2E'
},
{
name: 'Jason',id:4,
title: 'Junior F2E'
}
]
},
{
name: 'Back-End',id:5,
children: [
{
name: 'Mary',id:6,
title: 'Leader'
},
{
name: 'Gary',id:7,
title: 'Intern'
}
]
}
]
},
{
name: 'Design',id:8,
children: [{
name: 'Freeman',id:9,
title: 'Designer'
}]
},
{
name: 'S&S',id:10,
children: [{
name: 'Nikky',id:11,
title: 'Robot'
}]
}
];
app = angular.module('testApp', []).controller('treeTable', [
'$scope',
'$filter',
function ($scope, $filter) {
$scope.list = list;
//$scope.item.selected={};
$scope.initCheckbox = function (item, parentItem) {
return item.selected = parentItem && parentItem.selected || item.selected || false;
};
$scope.toggleCheckbox = function (item, parentScope) {
if (item.children != null) {
$scope.$broadcast('changeChildren', item);
}
if (parentScope.item != null) {
return $scope.$emit('changeParent', parentScope);
}
};
$scope.$on('changeChildren', function (event, parentItem) {
var child, i, len, ref, results;
ref = parentItem.children;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
child = ref[i];
child.selected = parentItem.selected;
if (child.children != null) {
results.push($scope.$broadcast('changeChildren', child));
} else {
results.push(void 0);
}
}
return results;
});
return $scope.$on('changeParent', function (event, parentScope) {
var children;
children = parentScope.item.children;
parentScope.item.selected = $filter('selected')(children).length === children.length;
parentScope = parentScope.$parent.$parent;
if (parentScope.item != null) {
return $scope.$broadcast('changeParent', parentScope);
}
});
}
]);
app.filter('selected', [
'$filter',
function ($filter) {
return function (files) {
return $filter('filter')(files, { selected: true });
};
}
]);
</script>
</head>
<body>
<div class="wrapper" ng-app="testApp" ng-controller="treeTable">
<table class="table-nested">
<tbody ng-class="{opened: item.opened}" ng-include="'table_tree.html'" ng-repeat="item in list"></tbody>
</table>
<script id="table_tree.html" type="text/ng-template">
<tr ng-class="{parent: item.children}" ng-init="parentScope = $parent.$parent; initCheckbox(item, parentScope.item)">
<td class="cell-name">
<div class="indent" ng-click="item.opened = !item.opened"></div>
<input ng-change="toggleCheckbox(item, parentScope)" ng-model="item.selected" type="checkbox" />
{{item.name}}
</td>
</tr>
<tr class="children" ng-if="item.children && item.children.length > 0">
<td colspan="4">
<table class="table-child">
<tbody ng-class="{opened: item.opened}" ng-include="'table_tree.html'" ng-init="level = level + 1" ng-repeat="item in item.children"></tbody>
</table>
</td>
</tr>
</script>
{{item.selected | json}}
</div>
</body>
check plunker here

Maybe you can do it like this:
JS:
$scope.seleceds = {};
$scope.initCheckbox = function (item, parentItem) {
return $scope.seleceds[item.id] = parentItem && $scope.seleceds[parentItem.id] || $scope.seleceds[item.id] || false;
};
$scope.toggleCheckbox = function (item, parentScope) {
if (item.children != null) {
$scope.$broadcast('changeChildren', item);
}
if (parentScope.item != null) {
return $scope.$emit('changeParent', parentScope);
}
};
$scope.$on('changeChildren', function (event, parentItem) {
var child, i, len, ref, results;
ref = parentItem.children;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
child = ref[i];
$scope.seleceds[child.id] = $scope.seleceds[parentItem.id];
if (child.children != null) {
results.push($scope.$broadcast('changeChildren', child));
} else {
results.push(void 0);
}
}
return results;
});
return $scope.$on('changeParent', function (event, parentScope) {
var children;
children = parentScope.item.children;
$scope.seleceds[parentScope.item.id] = $filter('selected')(children, $scope.seleceds).length === children.length;
parentScope = parentScope.$parent.$parent;
if (parentScope.item != null) {
return $scope.$broadcast('changeParent', parentScope);
}
});
Extra filter:
app.filter('selected', ['$filter', function ($filter) {
return function (files, obj) {
return $filter('filter')(files, function (value) {
return obj[value.id]
});
};
}]);
app.filter('objToArray', function () {
return function (input) {
var results = [];
for (var key in input) {
input[key] && results.push(Number(key))
}
return results;
}
});
HTML:
<input ng-change="toggleCheckbox(item, parentScope)" ng-model="seleceds[item.id]" type="checkbox"/>
And change {{item.selected | json}} to {{seleceds|objToArray}}
you can see a demo HERE

Related

AngularJS filter | orderBy with custom order

I am trying to order a list in a ng-repeat. What i want to do is set the order by hand, so i can say players first, coach second and waterboy last in the list. This is where i got but now got stuck with filters (kinda new to me):
$scope.Team= [{
Name: "Saras"
Role: "coach"
},{
Name: "Arya"
Role: "player"
},{
Name: "Adam"
Role: "waterboy"
},{
Name: "Theo"
Role: "player"
},{
Name: "Mark"
Role: "player"
},{
Name: "Oscar"
Role: "player"
},{
Name: "Tom"
Role: "player"
},{
Name: "Gus"
Role: "coach"
}];
<div ng-repeat="person in Team | orderBy:Role>
<div>{{person.Name}}</div>
</div>
i would like to orderBy 'Role' this way, where i can set the order
'player', 'coach', 'waterboy'
with the filter below i am able to set the order correct and add the label of the Role (coach, player etc.), however the labels sometimes get repeated. How could i fix that?
<div ng-repeat="person in Team | myOrder:'Role'>
<h2 ng-show="item.Role_CHANGED">{{item.Role}}</h2>
<p>{{person.Name}}</p>
</div>
app.filter('myOrder', function () {
return function (list, group_by) {
function CustomOrder(item) {
switch (item) {
case 'coach':
return 2;
case 'player':
return 1;
case 'waterboy':
return 3;
}
}
var filtered = [];
var prev_item = null;
var group_changed = false;
var new_field = group_by + '_CHANGED';
angular.forEach(list, function (item) {
group_changed = false;
if (prev_item !== null) {
if (prev_item[group_by] !== item[group_by]) {
group_changed = true;
}
} else {
group_changed = true;
}
if (group_changed) {
item[new_field] = true;
} else {
item[new_field] = false;
}
filtered.push(item);
prev_item = item;
});
filtered.sort(function (a, b) {
return (CustomOrder(a.Role) > CustomOrder(b.Role) ? 1 : -1);
});
return filtered;
};
})
try this example :
html :
<div class="test" ng-controller="Ctrl">
<div ng-repeat="division in divisions | orderBy:['group','sub']">{{division.group}}-{{division.sub}}</div>
<div>
js :
var app = angular.module('app', []);
function Ctrl($scope) {
$scope.divisions = [{'group':1,'sub':1}, {'group':2,'sub':10}, {'group':1,'sub':2},{'group':1,'sub':20},{'group':2,'sub':1},{'group':2,'sub':11}];
}
alright what i did to solve this is that i moved the sort function to the front when i GET the data from the JSON repsonse.
$http({
method: 'Get',
url: "https://xxxxxxx.azurewebsites.net/api/team/" + id_company
})
.success(function (data) {
var neworder = [];
function CustomOrder(item) {
switch (item) {
case 'player':
return 1;
case 'coach':
return 2;
case 'waterboy':
return 3;
}
}
angular.forEach(data, function (item) {
neworder.push(item);
});
neworder.sort(function (a, b) {
return (CustomOrder(a.Role) > CustomOrder(b.Role) ? 1 : -1);
});
$scope.Team = neworder;
});
after i just run the filter without the sort function;
<div ng-repeat="person in Team | myOrder:'Role'>
<h2 ng-show="item.Role_CHANGED">{{item.Role}}</h2>
<p>{{person.Name}}</p>
</div>
app.filter('myOrder', function () {
return function (list, group_by) {
var filtered = [];
var prev_item = null;
var group_changed = false;
var new_field = group_by + '_CHANGED';
angular.forEach(list, function (item) {
group_changed = false;
if (prev_item !== null) {
if (prev_item[group_by] !== item[group_by]) {
group_changed = true;
}
} else {
group_changed = true;
}
if (group_changed) {
item[new_field] = true;
} else {
item[new_field] = false;
}
filtered.push(item);
prev_item = item;
});
return filtered;
};
})
it's a bit of a work-around but it did the job
just to be complete, if you just want to sort the ng-repeat in a filter and no need for the lables. you can just do this:
<div ng-repeat="person in Team | Sorter:'Role'>
<p>{{person.Name}}</p>
</div>
app.filter('Sorter', function () {
function CustomOrder(item) {
switch (item) {
case 'player':
return 1;
case 'coach':
return 2;
case 'waterboy':
return 3;
}
}
return function (items, field) {
var filtered = [];
angular.forEach(items, function (item) {
filtered.push(item);
});
filtered.sort(function (a, b) {
return (CustomOrder(a.Role) > CustomOrder(b.Role) ? 1 : -1);
});
return filtered;
};
});

how can i remove ng repeat data in angularjs

I have two table and i want to append second table data in $scope.notiData and how can i remove ng repeat data if i click remove symbol X. I have some code but it is not working.please help anyone
http://jsfiddle.net/A6bt3/118/
var app = angular.module('myApp', []);
function checkBoxCtrl($scope) {
$scope.tableOne = [{
firstname: 'robert',
value: 'a'
}, {
firstname: 'raman',
value: 'b'
}, {
firstname: 'kavi',
value: 'c'
}, {
firstname: 'rorank',
value: 'd'
}
];
$scope.tableTwo = [];//the table to be submitted
function removeitems(tableRef) { //revmove items from tableRef
var i;
for (i = tableRef.length - 1; i >= 0; i -= 1) {
if (tableRef[i].checked) {
tableRef.splice(i, 1);
}
}
}
$scope.btnRight = function () {
//Loop through tableone
$scope.tableOne.forEach(function (item, i) {
// if item is checked add to tabletwo
if (item.checked) {
$scope.tableTwo.push(item);
}
})
removeitems($scope.tableOne);
}
$scope.btnAllRight = function () {
$scope.tableOne.forEach(function (item, i) {
item.checked = true;
$scope.tableTwo.push(item);
})
removeitems($scope.tableOne);
}
$scope.btnLeft = function () {
$scope.tableTwo.forEach(function (item, i) {
if (item.checked) {
$scope.tableOne.push(item);
}
})
removeitems($scope.tableTwo);
}
$scope.btnAllLeft = function () {
$scope.tableTwo.forEach(function (item, i) {
item.checked = true;
$scope.tableOne.push(item);
})
removeitems($scope.tableTwo);
}
$scope.done = function () {
//alert(angular.toJson($scope.tableTwo));
$scope.notiData = $scope.tableTwo;
}
$scope.removeRow = function () {
}
};
do it like that :
$scope.removeRow = function (item) {
var index = $scope.notiData.indexOf(item);
$scope.notiData.splice(index, 1);
}
and to merge the arrays:
$scope.done = function () {
angular.extend($scope.notiData, $scope.tableTwo);
}
dont forget to initialize notiData :
$scope.notiData = [];
to resolve the problem of redirecting remove the target="_blank" and change href:
<a ng-repeat="data in notiData" class="emailButton" href="#">{{data.firstname}}<div class="" ng-click="removeRow()">X</div></a>
http://jsfiddle.net/A6bt3/122/
html element
<a ng-repeat="data in notiData" class="emailButton">{{data.firstname}}
<div class="" ng-click="removeRow($index)">X</div>
</a>
remove function
$scope.removeRow = function (index) {
$scope.notiData.splice(index,1);
}
pass the index of the data element in the removeRow() function from the view.
<a ng-repeat="data in notiData" class="emailButton" href="#">{{data.firstname}}
<div class="" ng-click="removeRow($index)">X</div>
</a>
and in controller filter the passed index element from the list.
$scope.removeRow = function (index) {
$scope.notiData = $scope.notiData.filter(function(elem){
return elem !== $scope.notiData[index]
})
}
fiddle : http://jsfiddle.net/w5upkawt/

v2.canGoForward is not a function

I am trying to move the code vm.canGoForward from my controller to a service to hide the implementation details.
BEFORE CODE CHANGE
This worked fine.
View:
<button ng-disabled="!vm.canGoForward()" class="btn btn-primary" name="next" type="button" ng-click="vm.gotoStep(vm.currentStep + 1)">
Controller:
var vm = this;
vm.currentStep = 1;
vm.steps = WizardService.getWizardSteps(vm.formData);
vm.canGoForward = function() {
var res = true,
i,
nextStateIndex = vm.currentStep + 1;
if (nextStateIndex > vm.steps.length) {
return false;
}
for (i = 1; res && i <= nextStateIndex; i++) {
res = (res && vm.steps[i-1].isReady());
}
return !!res;
};
Service
var wizardService = {
getWizardSteps: getWizardSteps
};
return wizardService;
function getWizardSteps(formData) {
var wizardSteps = [
{
step: 1,
name: 'Name',
template: 'views/wizard/step1.html',
isReady: function() { return true; }
},
{
step: 2,
name: 'Email',
template: 'views/wizard/step2.html',
isReady: function() { return formData.firstName && formData.lastName; }
},
{
step: 3,
name: 'Job Category',
template: 'views/wizard/step3.html',
isReady: function() { return formData.email; }
}
];
return wizardSteps;
}
AFTER CODE CHANGE
View
Remains the same
Controller
var vm = this;
vm.currentStep = 1;
vm.steps = WizardService.getWizardSteps(vm.formData);
vm.canGoForward = WizardService.canGoForward(vm.currentStep, vm.steps);
Service
var wizardService = {
getWizardSteps: getWizardSteps,
canGoForward: canGoForward
};
return wizardService;
function getWizardSteps(formData) {
var wizardSteps = [
{
step: 1,
name: 'Name',
template: 'views/wizard/step1.html',
isReady: function() { return true; }
},
{
step: 2,
name: 'Email',
template: 'views/wizard/step2.html',
isReady: function() { return formData.firstName && formData.lastName; }
},
{
step: 3,
name: 'Job Category',
template: 'views/wizard/step3.html',
isReady: function() { return formData.email; }
}
];
return wizardSteps;
}
function canGoForward(currentStep, steps) {
console.log(steps);
var res = true,
i,
nextStateIndex = currentStep + 1;
if (nextStateIndex > steps.length) {
return false;
}
for (i = 1; res && i <= nextStateIndex; i++) {
res = (res && steps[i-1].isReady());
}
return !!res;
}
I now get the following error: TypeError: v2.canGoForward is not a function. How can I resolve it?
In your second version, the following line will actually call WizardService.canGoForward on the spot, not assign it:
vm.canGoForward = WizardService.canGoForward(vm.currentStep, vm.steps);
What gets assigned is the return value of that call, which obviously is not a function, hence the error message when a call is attempted later.
If you want to assign the function, and ensure the arguments get passed when it is called later, then use bind:
vm.canGoForward = WizardService.canGoForward.bind(WizardService, vm.currentStep, vm.steps);

Adding and removing items from ng-repeat, simple cart implementation, using angular

I'm trying to achieve an array with items that displays as a title and image and when the user click the title the item should be added to a div. However, I canĀ“t make it to work as when I click on "Add me", there is only a blank item added, with no image and no title.
I got the delete-function to work but not the add-function.
Here is what I got:
The "Add me"-button and the list of items
<li ng-repeat="item in items.data">
{{item.title}} <img ng-src="{{ item.image }}" /><a ng-click="deleteItem($index)" class="delete-item">x</a>
<button ng-click="addItem()">Add Me</button>
</li>
The array
var myApp = angular.module("myApp", []);
myApp.factory("items", function () {
var items = {};
items.data = [{
title: "Item 1",
image: "img/item01.jpg"
}, {
title: "Item 2",
image: "img/item02.jpg"
}, {
title: "Item 3",
image: "img/item03.jpg"
}, {
title: "Item 4",
image: "img/item04.jpg"
}];
return items;
});
And the functions for add and delete
function ItemsController($scope, items) {
$scope.items = items;
$scope.deleteItem = function (index) {
items.data.splice(index, 1);
}
$scope.addItem = function () {
items.data.push({
title: $scope.items.title
});
}
}
You need to pass the item under iteration.
<button ng-click="addItem(item)">Add Me</button>
and add the title to the newly added:
$scope.addItem = function(item) {
items.data.push({
title: item.title
});
}
it is better not to use $index (it can cause issue when used with filters eg: orderBy filter) instead just pass the item to delete and:
$scope.deleteItem = function(item) {
items.data.splice(items.indexOf(item), 1);
}
You also have an invalid html, li must be a child of ul or ol.
A sample implementation:
function($scope, items) {
$scope.items = items;
$scope.cart = [];
$scope.deleteItem = function(item) {
var cart = $scope.cart;
//Get matched item from the cart
var match = getMatchedCartItem(item);
//if more than one match exists just reduce the count
if (match.count > 1) {
match.count -= 1;
return;
}
//Remove the item if current count is 1
cart.splice(cart.indexOf(item), 1);
}
$scope.addItem = function(item) {
//Get matched item from the cart
var match = getMatchedCartItem(item), itemToAdd ;
//if item exists just increase the count
if (match) {
match.count += 1;
return;
}
//Push the new item to the cart
itemToAdd = angular.copy(item);
itemToAdd.count = 1;
$scope.cart.push(itemToAdd);
}
function getMatchedCartItem(item) {
/*array.find - Find the shim for it in the demo*/
return $scope.cart.find(function(itm) {
return itm.id === item.id
});
}
Demo
angular.module('app', []).controller('ctrl', function($scope, items) {
$scope.items = items;
$scope.cart = [];
$scope.deleteItem = function(item) {
var cart = $scope.cart;
var match = getMatchedCartItem(item);
if (match.count > 1) {
match.count -= 1;
return;
}
cart.splice(cart.indexOf(item), 1);
}
$scope.addItem = function(item) {
var match = getMatchedCartItem(item);
if (match) {
match.count += 1;
return;
}
var itemToAdd = angular.copy(item);
itemToAdd.count = 1;
$scope.cart.push(itemToAdd);
}
function getMatchedCartItem(item) {
return $scope.cart.find(function(itm) {
return itm.id === item.id
});
}
}).factory("items", function() {
var items = {};
items.data = [{
id: 1,
title: "Item 1",
image: "img/item01.jpg"
}, {
id: 2,
title: "Item 2",
image: "img/item02.jpg"
}, {
id: 3,
title: "Item 3",
image: "img/item03.jpg"
}, {
id: 4,
title: "Item 4",
image: "img/item04.jpg"
}];
return items;
});
if (!Array.prototype.find) {
Array.prototype.find = function(predicate) {
if (this == null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return value;
}
}
return undefined;
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<ul>
<li ng-repeat="item in items.data" id="item{{item.id}}">
{{item.title}}
<img ng-src="{{ item.image }}" />
<button ng-click="addItem(item)">Add Me</button>
</li>
</ul>
<p>Cart:</p>
<ul>
<li ng-repeat="item in cart">
{{item.title}} | Count: {{item.count}}
<a ng-click="deleteItem(item)" class="delete-item">X</a>
</li>
</ul>
</div>
I am using Array.prototype.find, you would need to add the shim (as mentioned in the demo) to be able to make it work for non supported browsers.

Why does just rendering the array work, but ng-repeat over it fails?

first version works, second version fails
Uncaught Error: 10 $digest() iterations reached. Aborting!
<table data-role="parsed config viewer">
<thead>
<tr>
<th>name</th>
<th>client strings</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="svc in services">
<td>{{svc.name}}</td>
<td>{{svc.remoteClients()}}</td>
<td>
<button ng-click="getJson('http://'+nodeHost+'/parsed.config?host='+sandbox+'&base='+machineBasePath+case+'&path='+svc.dir+'Web.config',svc,false);">Refresh</button>
</td>
</tr>
<tr ng-repeat="svc in services">
<td>{{svc.name}}(local)</td>
<td>
<ul>
<li ng-repeat="slClient in svc.localClients()">{{slClient}}</li>
</ul>
</td>
<td>
<button ng-click="getJson('http://'+nodeHost+'/parsed.config?host=localhost&base='+localpath+'&path='+svc.dir+'Web.config',svc,true);">Refresh</button>
</td>
</tr>
</tbody>
</table>
the javascript
if (!Array.prototype.arrayFirst) {
Array.prototype.arrayFirst = function (predicate, predicateOwner) {
var array = this || [];
for (var i = 0, j = array.length; i < j; i++)
if (predicate.call(predicateOwner, array[i])) return array[i];
return null;
};
}
if (!Array.prototype.arrayMap) {
Array.prototype.arrayMap = function (mapping) {
var array = this || [];
var result = [];
for (var i = 0, j = array.length; i < j; i++)
result.push(mapping(array[i]));
return result;
};
}
var Service = function (name, path, svc, notes) {
var self = this;
this.name = name;
this.path = path;
this.svc = svc;
this.notes = notes;
this.dir = "\\" + path.replace("/", "\\") + "\\";
this.url = "/" + path.replace("\\", "/") + "/" + svc;
var getConfig = function (local) {
var parsed = local ? self.parsedlocal : self.parsed;
return parsed;
};
var getClients = function (local) {
var parsed = getConfig(local);
if (!parsed) return ["not attempted"];
if (!parsed.configuration) return ["no configuration"];
if (!parsed.configuration['system.serviceModel']) return ["No Service Model"];
var clients = parsed.configuration['system.serviceModel'][0].
client[0].endpoint.arrayMap(function (a) {
return {
name: a.$.name,
address: a.$.address
};
});
return clients;
};
this.localClients = function () {
console.log('getting local clients');
return getClients(true);
};
this.remoteClients = function () {
console.log('getting remote clients');
return getClients(false);
};
};
var PublishCtrl = function ($scope, $http, $timeout) {
window.publishCtrl = $scope;
$scope.getJson = function (uri, svc, local) {
console.log('get json!');
//window.parsed= data;
if (local) {
var newParsed = {
configuration: {
'system.serviceModel': [{
client: [{
endpoint: [{
$: {
name: 'localabc'
}
}, {
$: {
name: 'localcba'
}
}]
}]
}]
}
};
if (newParsed != svc.parsedLocal) {
console.log('updating parsedlocal');
svc.parsedlocal = newParsed;
}
} else {
console.log('updating parsed');
svc.parsed = {
configuration: {
'system.serviceModel': [{
client: [{
endpoint: [{
$: {
name: 'remoteabc'
}
}, {
$: {
name: 'remotecba'
}
}]
}]
}]
}
};
}
};
$scope.services = [
new Service("Wcf Portal", "Services.Host", "WcfPortal.svc"),
new Service("Registration Portal", "Services.Host.Registration", "WcfPortal.svc"),
new Service("Program Pricing", "Services.Host.ProgramPricing", "ProgramPricingService.svc"),
new Service("Data Lookup", "DataLookupService", "DataLookupService.svc", "path depth issues"),
new Service("DocumentPrintingService", "Services.Host.PrintingService", "DocumentPrintingService.svc")];
};
http://jsfiddle.net/Maslow/j9Jes/

Categories

Resources