How to set index values to child rows in angular js - javascript

I have a requirement where I need to add index values for child rows. I have Group rows under which there will be child rows. I am ng-repeat and I am using $index for child's as shown:
HTML code:
<table ng-repeat="node in data">
<tr> <td> {{node.groupName}} </td> </tr>
<tbody ng-model="node.nodes">
<tr ng-repeat="node in node.nodes"> <td> {{$index}} </td> </tr>
</table>
But it is displaying as shown:
But I want it to display as shown:
I am new to Angular JS and not getting how to display it like this. How am I supposed to do that. Please help.

As far as I understood your question, you'd like to have something like that:
<table ng-repeat="group in data">
<thead>
<th> {{group.name}} </th>
</thead>
<tbody ng-repeat="item in group.items">
<tr>
<td>{{getIndex($parent.$index - 1, $index)}} | {{item}} </td>
</tr>
</tbody>
</table>
$scope.data = [
{name: 'Group1', items: ['a','b']},
{name: 'Group2', items: [1,2,3]},
{name: 'Group3', items: ['x', 'xx', 'xxx', 'xxxx']}
];
$scope.getIndex = function(previousGroupIndex, currentItemIndex){
if(previousGroupIndex >= 0){
var previousGroupLength = getPreviousItemsLength(previousGroupIndex);
return previousGroupLength + currentItemIndex;
}
return currentItemIndex;
};
function getPreviousItemsLength(currentIndex){
var length = 0;
for (var i = 0; i <= currentIndex; i++){
length += $scope.data[i].items.length;
}
return length;
// or even better to use Array.prototype.reduce() for that purpose
// it would be shorter and beautiful
// return $scope.data.reduce(function(previousValue, currentGroup, index){
// return index <= previousGroupIndex ? previousValue + currentGroup.items.length : previousValue;
// }, 0);
}
You need to play with $parent.$index property and use some math :) in order to achieve that.
It would look like the following:
Check out this JSFiddle to see live example.

Related

AngularJS : ng-repeat with uniques values in particular method

I have one object like this
$scope.listvalues = [{ name:A, id:101 },
{ name:B, id:102 },
{ name:A, id:103 },
{ name:C, id:101 },
{ name:A, id:102 },
{ name:B, id:103 }];
I need to print this object in following structure
name |101 | 102 |103 |
-----|----|-----|---------
A |YES | YES |YES |
-----|----|-----|------------
B |No | YES |YES |
-----|----|-----|-----------
C |YES | NO |NO |
Here i need to print the Name "A" in unique and also need to indicate the A is available for which Id. Is it possible to do with angularjs ng-repeat?. Any one please suggest...
You can, but you would have to write a filter that changes the structure of your data to the following:
$scope.data = [
{A: {'101': true, '102': true, '103': true}},
{B: {'101': false, ...}},
{C: ...}
]
And then you can write your table like this:
<table>
<tr>
<th>name</th>
<th ng-repeat="(column, value) in data[0]">{{column}}</th>
</tr>
<tr ng-repeat="row in data">
<td ng-repeat="(column, value) in data[0]">{{row[column] ? 'Yes' : 'No'}}</td>
</tr>
</table>
Example filter:
yourModule.filter('makeNgRepeatable', function(){
return function(oldStructure) {
// Add code here to convert oldStructure to newStructure.
return newStructure;
}
});
In your controller, inject makeNgRepeatableFilter and do
$scope.data = makeNgRepeatableFilter([{ name:A, id:101 }, ...]);
You can pack this into a table and then resolve with multiple ng-repeats which cell is YES or NO.
Take a look at this plnkr, it demonstrates how this could be achieved.
http://plnkr.co/edit/QI8ZrsbwYuJUeV4DNWGl?p=preview
First you collect all distinct ids and names.
$scope.names = $scope.listvalues.map(function(d){return d.name}).unique();
$scope.ids = $scope.listvalues.map(function(d){return d.id}).unique();
Note: In the plnkr I defined the functions unique and contains. If you use some other libraries like underscore those functions may already be present.
Then you define a function to determine if a specific cell should be true or false.
$scope.hasValue = function(name, id) {
for(var i = 0; i < $scope.listvalues.length; ++i)
if($scope.listvalues[i].name === name && $scope.listvalues[i].id === id)
return true;
return false;
}
However, it would be simpler if you can convert your listvalues into a reasonable structure. This would prevent some of the overhead.
With your array structure you might need some additional helper array/objects. In your case it could look like this:
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th ng-repeat="th in values">{{th}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="obj in names">
<td>
{{obj.name}}
<div><small>{{obj}}</small></div>
</td>
<td>{{obj.ids.indexOf(values[0]) > -1 ? 'YES' : 'NO'}}</td>
<td>{{obj.ids.indexOf(values[1]) > -1 ? 'YES' : 'NO'}}</td>
<td>{{obj.ids.indexOf(values[2]) > -1 ? 'YES' : 'NO'}}</td>
</tr>
</tbody>
</table>
where helper objects are constructed like:
function initData() {
var map = {values: {}, names: {}},
values = [],
names = [];
$scope.listvalues.forEach(function(obj) {
if (!map.values[obj.id]) {
values.push(obj.id);
map.values[obj.id] = true;
}
if (!map.names[obj.name]) {
names.push(obj);
obj.ids = [obj.id];
map.names[obj.name] = obj;
}
else {
map.names[obj.name].ids.push(obj.id);
}
});
$scope.values = values;
$scope.names = names;
}
initData();
Demo: http://plnkr.co/edit/wWJOjtzstUDKjl9V6hCy?p=preview

binding returned value from JavaScript function to ng-model

im trying to get returned value from function and display it using ng-model but column is empty and no error on log. the value should be the max score in the users array (highest score :46).
any idea why isn't it working?
HTML:
<div ng-controller="firstCtrl">
<table class="table table-hover">
<thead>
<td>Name</td>
<td>Score</td>
<td>Difference</td>
</thead>
<tr ng-repeat="user in users | orderBy: '-score'">
<td >{{user.name}}</td>
<td >{{user.score}}</td>
<td ng-model="maxScore">{{max}}</td>
</tr>
</table>
</div>
JS:
var myApp = angular.module("myApp",[]);
myApp.controller ('firstCtrl', function($scope, PersonService){
$scope.users = PersonService.list();
$scope.maxScore = function(users){
PersonService.getMaxScore(users);
}
})
myApp.service('PersonService',function(){
var uid = 1;
var users = [{
id: 0,
'name': 'John',
'score': 46
},{
'name': 'Harry',
'score': 45
},{
'name': 'Sam',
'score': 32
}];
this.list = function () {
return users;
}
this.getMaxScore = function (arr) {
var max;
for (var i = 0; i < arr.length; i++) {
if (arr[i].score > (max || 0))
max = arr[i].score;
}
return max;
}
})
Checkout this plnkr
first you are not calling as method. And second you are not returning from method.
Change your html to following
<div ng-controller="firstCtrl">
<table class="table table-hover">
<thead>
<td>Name</td>
<td>Score</td>
<td>Difference</td>
</thead>
<tr ng-repeat="user in users | orderBy: '-score'">
<td >{{user.name}}</td>
<td >{{user.score}}</td>
<td>{{maxScore(users)}}</td>
</tr>
</table>
</div>

Unbinding ng-repeat scope

I have two <tr>s and an ng-repeat on each, but both perform the same operation on their child elements, like so:
<tr ng-repeat="item in data : filterFunc" ng-if="mode === 'something'">
<td>{{item.name}}</td>
</tr>
<tr ng-repeat="item in data : filterFunc" ng-if="mode === 'somethingelse'">
<td>{{item.name}}</td>
</tr>
Imagine, there are two types of data sets and which one is to be rendered is decided by the mode property at run time. So, either the first <tr> is rendered in the DOM or the other one.
Initially the first <tr> will be rendered and the associated filterFunc function will work properly. There's a simple drop down which has two options, one each for the two modes. If you select the other mode, the second <tr> will be rendered and first one will be removed from the DOM.
The problem is, the filterFunc now is bound to both the <tr>s and operates on both of them.
How do I unbind the scope or watchers for the first one let it be bound only to the second one? or any one of them at any point of time? Here's the fiddle http://jsfiddle.net/6kx4ojL4/
Note: For the sake of simplicity, I have simply returned the passed-in data object as-is in filterFunc. Check the output in browser console. It gets called twice, i.e. for both data sets.
It is not clear what you want to do exactly from your description. My first thought is that you could separate your modes in ng-if, say, use mode1 and mode2, so you can control two <tr>s separately..
Use ng-show instead of ng-if
<tr ng-repeat="item in data : orderBy: 'name'" ng-show="mode === 'something'">
<td>{{item.name}}</td>
</tr>
<tr ng-repeat="item in data : orderBy: 'name'" ng-show="mode === 'somethingelse'">
<td>{{item.name}}</td>
</tr>
How about this. Check this JSFiddle: http://jsfiddle.net/vxcjw45d/1/
If it's not what are you looking for tell me - I will delete it :)
HTML:
<body ng-app="myApp">
<table ng-controller="myController">
<tr ng-repeat="item in data | orderBy: 'name'"
ng-if="mode === 'something'">
<td>{{ item.name }}</td>
</tr>
<tr ng-repeat="item in data | orderBy: 'name'"
ng-if="mode === 'somethingelse'">
<td>{{ item.age }}</td>
</tr>
<tr>
<td>
<button ng-click="changeMode()">
Change Mode
</button>
</td>
</tr>
</table>
</body>
JS:
var myApp = angular.module('myApp', []);
myApp.controller('myController', function($scope) {
$scope.data = [
{ name: 'John', age: 21 },
{ name: 'Doe', age: 33 }
];
$scope.mode = 'something';
$scope.changeMode = function() {
if ($scope.mode === 'something') {
$scope.mode = 'somethingelse';
} else if ($scope.mode === 'somethingelse') {
$scope.mode = 'something'
}
};
});

how to add an array to localstore in angularjs

I am trying to build a shopping cart. I want to add the array invoice to localstorage so that i could access it later.
I guess there are some errors with this form of approach
angular.module('myApp', ['ngCookies']);
function CartForm($scope, $cookieStore) {
$scope.invoice.items = $cookieStore.get('items');
$scope.addItem = function() {
$scope.invoice.items.push({
qty: 1,
description: '',
cost: 0
});
$scope.invoice.items = $cookieStore.put('items');
},
$scope.removeItem = function(index) {
$scope.invoice.items.splice(index, 1);
$scope.invoice.items = $cookieStore.put('items');
},
$scope.total = function() {
var total = 0;
angular.forEach($scope.invoice.items, function(item) {
total += item.qty * item.cost;
})
return total;
}
}
HTML contains a button , which pushes the new items to the array which gets automatically binded.
<div ng:controller="CartForm">
<table class="table">
<tr>
<th>Description</th>
<th>Qty</th>
<th>Cost</th>
<th>Total</th>
<th></th>
</tr>
<tr ng:repeat="item in invoice.items">
<td><input type="text" ng:model="item.description"class="input-small"></td>
<td><input type="number" ng:model="item.qty" ng:required class="input-mini"> </td>
<td><input type="number" ng:model="item.cost" ng:required class="input-mini"> </td>
<td>{{item.qty * item.cost | currency}}</td>
<td>
[<a href ng:click="removeItem($index)">X</a>]
</td>
</tr>
<tr>
<td><a href ng:click="addItem()" class="btn btn-small">add item</a></td>
<td></td>
<td>Total:</td>
<td>{{total() | currency}}</td>
</tr>
</table>
</div>
Local stage saves only strings, not complex objects.
What you can do, therefore, is stringify it when saving and re-parse it when accessing it.
localStorage['foo'] = JSON.stringify([1, 2, 3]);
Be aware that the stringify process will strip out any unsuitable elements in the array, e.g. functions.
To re-parse it:
var arr = JSON.parse(localStorage['foo']);
localStorage["items"] = JSON.stringify(items);
update: you can retrieve it as follows: `var items:
localStorage.getItem('items');
source
localStorage supports only strings, so that why you must use JSON.stringify() and JSON.parse() to work via localStorage.
var p = [];
p[0] = "some";
localStorage["p"] = JSON.stringify(p);
For your code:
var items = [{
qty: 10,
description: 'item',
cost: 9.95}];
localStorage.setItem("items", JSON.stringify(items));
// get
var items = JSON.parse(localStorage.getItem("items"));
localStorage supports only strings, so you must use the following code:
var p = [];
p[0] = "some";
localStorage["p"] = JSON.stringify(p);

AngularJS orderBy Function With No Sorting

I am attempting to use a custom orderBy function. Initially, I want the data to appear in the order it was added to $scope.rows, and only after clicking on a column heading should it order by a specific property. Here's my fiddle:
http://jsfiddle.net/S8M4c/
Here's my view:
<table ng-app ng-controller="ctrl">
<tr>
<th><a ng-click="orderBy = 'id'">ID</a></th>
<th><a ng-click="orderBy = 'name'">Name</a></th>
</tr>
<tr ng-repeat="row in rows | orderBy:mySort">
<td>{{row.object.id}}</td>
<td>{{row.object.name}}</td>
</tr>
</table>
Here's my controller:
function ctrl($scope)
{
// Initially, we don't sort by anything
$scope.orderBy = "";
$scope.rows = [];
// Add some rows
for(var i = 10;i < 30;i++)
{
$scope.rows.push({settings: {foo: true}, object: {id: i, name: "Name " + i}})
};
$scope.mySort = function(row)
{
if($scope.orderBy != "")
{
return row.object[$scope.orderBy];
}
// What do I return here??
return "";
}
}
In the case that $scope.orderBy isn't set and I want to return $scope.rows in it's original order, what do I return in $scope.mySort? I cannot return row.object.id because the rows are not guaranteed to be added in order of their ID. Running my code as is on Chrome 32, the first row that appears has an ID of 20, which is the halfway row.
return $scope.rows.indexOf(row);
(Fiddle.)
You can also do this with out-of-the-box orderBy by providing a function returning that as the default predicate:
Controller:
$scope.mySort = $scope.unsorted = function(row)
{
return $scope.rows.indexOf(row);
}
View:
<div ng-app ng-controller="ctrl">
<table>
<tr>
<th><a ng-click="mySort = 'object.id'">ID</a></th>
<th><a ng-click="mySort = 'object.name'">Name</a></th>
</tr>
<tr ng-repeat="row in rows | orderBy:mySort">
<td>{{row.object.id}}</td>
<td>{{row.object.name}}</td>
</tr>
</table>
<button ng-click="mySort = unsorted;">Original Sort</button>
</div>
Fiddle here. (I've changed the numbers used in the objects so that sort by id, sort by name, and the original sort aren't all the same.)
I think you have to write your own sortby function. The original angulars orderBy is a regular filter that returns the sorted array. Your filter may look something like this:
.filter('mySort', function(){
return function(values, param){
if(param===''){
return values;
}else{
// very important! create a copy of the array - otherwise
// the $wtachCollection function will fire to often!
var arrayCopy = [];
for ( var i = 0; i < values.length; i++) { arrayCopy.push(values[i]); }
return arrayCopy.sort(function(a,b){
var v1 = a.object[param];
var v2 = b.object[param];
// you know best how to sort it!
if (v1 === v2) return 0;
return v1 < v2 ? -1 : 1;
});
}
}
})
You can use this filter in this way:
<table ng-app="myApp" ng-controller="ctrl">
<tr>
<th><a ng-click="orderBy = 'id'">ID</a></th>
<th><a ng-click="orderBy = 'name'">Name</a></th>
</tr>
<tr ng-repeat="row in rows | mySort:orderBy">
<td>{{row.object.id}}</td>
<td>{{row.object.name}}</td>
</tr>
</table>
here is your modified fiddle: http://jsfiddle.net/spRf6/ I have changed the names a little bit so you may see that the sorting works.
Create a copy of the objects array and the ordering then becomes trivial:
Controller:
$scope.objects = [];
angular.forEach($scope.rows, function(row){
$scope.objects.push(row.object);
});
View:
<tr ng-repeat="object in objects | orderBy:orderBy">
<td>{{object.id}}</td>
<td>{{object.name}}</td>
</tr>
No need for the mySort function.

Categories