how to count items in nested ng-repeat without $index - javascript

I want to count iteration of ng-repeat, when condition match.
I've tried $index but it print for all itration/items in nested ng-repeat
Fiddle link :https://jsfiddle.net/gdr7p1zj/1/
<tbody ng-controller="MainCtrl">
<tr ng-repeat-start="a in test1">
<td>{{a.categoryName}}(count_here)</td>
</tr>
<tr ng-repeat-end ng-repeat="b in test" ng-if="a.categoryId==b.categoryId">
<td>{{b.name}}</td>
</tr>
</tbody>
i want like this
category_one(4) <=item count 4 items in this category so 4 will display
item1
item2
item3
item4
category_two(2)
item5
item6
<!-- this is in controller -->
$scope.test1=[{
categoryId:'1',categoryName:'category one'
},
{
categoryId:'2',categoryName:'category two'
}]
$scope.test = [
{categoryId:'1',name:'cate 1 elem0'},
{categoryId:'1',name:'cate 1 elem1'},
{categoryId:'2',name:'cate 2 elem'}
];
});

An option is to create a function (getCount) in the controller which do the count, something like this:
$scope.getCount = function(categoryId) { // returns the count by matching by categoryId
return $scope.test.filter(function(element) { // first, filter elements in `test`
return element.categoryId === categoryId; // matching by categoryId
}).length; // then, return the count of how many results we got after the filter
}
And in the html call that function like this:
<tbody ng-controller="MainCtrl">
<tr ng-repeat-start="a in test1">
<td>{{a.categoryName }} ({{getCount(a.categoryId)}})</td> <!-- <-- call the function in order to display the count -->
</tr>
<tr ng-repeat-end ng-repeat="b in test" ng-if="a.categoryId == b.categoryId">
<td>{{b.name}}</td>
</tr>
</tbody>
See a demo here: https://jsfiddle.net/lealceldeiro/v9gj1ok4/11/

Thanks for your help. But i get expected output without any functions call or filters
Here fiddle Link: https://jsfiddle.net/wk3nzj96/
htmlCode:
<div ng-app='myapp' >
<div ng-controller="MainCtrl">
<table ng-init="$scope.counter=0">
<tr ng-repeat-start="cate in mainCategory">
<td> {{cate.name}} ({{$scope.counter[$index]}})</td></tr>
<tr ng-repeat="itemsItr in items" ng-init="$scope.counter[$parent.$parent.$index]=$scope.counter[$parent.$parent.$index]+1" ng-if="itemsItr.mid==cate.id">
<td>{{itemsItr.name}}</td>
</tr>
<tr ng-repeat-end ng-if="false"></tr>
</table>
</div>
</div>
and ControllerCode:
(function() {
angular.module('myapp', []).controller('MainCtrl', function($scope) {
$scope.mainCategory = [
{ name: "categoryOne",id:1 },
{ name: "categoryTwo",id:2 }
];
$scope.items = [
{ name: "item1FromCateOne" ,mid:1 },
{ name: "item2FromCateOne",mid:1 },
{ name: "item3FromCateOne" ,mid:1 },
{ name: "item1FromCateTwo",mid:2 }
];
});
Is this Standard way to do this?

Related

Angular ng-repeat value set

I use ng-repeat to construct table with radio buttons. The idea is to assign to each radio value the position of the object inside the original array (before ordering). When I use $index it assigns position in the ordered array and not original one. How to assign the right index,original one?
<tr class="restTable" data-ng-repeat="person in persons|orderBy:'name'">
<td> {{ person.name}}</td>
<td> <input type="radio" name="radio" ng-model="$parent.selectedPerson" value="{{$index}}"/></td>
</tr>
As I wrote in the comment:
$index is relative to to the current element in the loop and since you are sorting the array then you need to save a reference on the object itself from the directive (You can use person.id for example (If you have a unique id for each person).
You can save a reference to the selected person via ngValue
angular.module('app', []).controller('ctrl', function($scope) {
$scope.selected = { person: null };
$scope.persons = [{id: 1, name: "person1"}, {id: 2, name: "person2"}, {id: 3, name: "person3"}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<table>
<tr class="restTable" data-ng-repeat="person in persons|orderBy:'name'">
<td> {{ person.name}}</td>
<td> <input type="radio" name="radio" ng-model="selected.person" ng-value="person"/></td>
</tr>
</table>
<hr>
<p>Selected Person:</p>
<pre ng-bind="selected.person | json"></pre>
</div>
Here i'm using the ngValue and saving a reference to the selected object inside the loop. I don't care about the current position of the object because angularjs is making sure the selected person will be available in the controller via $scope.selected.person.
If you want to pre select a person, replace
$scope.selected = { person: null };
With
$scope.selected = { person: $scope.persons[1] };
But don't forget to declare $scope.persons before! Put that line after you declared the array in your controller. Example:
angular.module('app', []).controller('ctrl', function($scope) {
$scope.persons = [{id: 1, name: "3person1"}, {id: 2, name: "1person2"}, {id: 3, name: "4person3"}];
$scope.selected = { person: $scope.persons[1] };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<table>
<tr class="restTable" data-ng-repeat="person in persons|orderBy:'name'">
<td> {{ person.name}}</td>
<td> <input type="radio" name="radio" ng-model="selected.person" ng-value="person"/></td>
</tr>
</table>
<hr>
<p>Selected Person:</p>
<pre ng-bind="selected.person | json"></pre>
</div>
$index won't work as it represents index of loop, not the index of item in array. So to fix this you could have index property in source or you could write a function to return related index.
var app = angular.module('app', []);
app.controller('ctrl', ['$scope', function(scope) {
scope.persons = [{
name: 'ABC index 0'
}, {
name: 'EFG index 1'
}, {
name: 'XYX index 2'
}];
scope.selectedPerson = "1";
scope.getIndex = function(item) {
return scope.persons.indexOf(item);
}
}])
angular.bootstrap(document.body, ['app']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller="ctrl">
Selected Person:-
<pre>{{persons[selectedPerson] | json}}</pre>
<hr/>
<table>
<tr class="restTable" data-ng-repeat="person in persons|orderBy:'name'">
<td> {{ person.name}}</td>
<td>
<input type="radio" name="radio" ng-model="$parent.selectedPerson" value="{{$parent.getIndex(person)}}" />
</td>
</tr>
</table>
</div>

Ng-repeat within an ng-repeat-start

My data:
$scope.members = [
{
projects: [...]
},
{
projects: [...]
}, ...
] // $scope.members.length == 15
My code:
<tr>
<th ng-repeat-start="member in members" ng-repeat="project in member.projects" ng-repeat-end ng-if="member.projects" >
{{project.name}}
</th >
</tr>
No element is generated. I checked the Elements in Chrome developer tool, and I found these in the <tr> tag comments like this:
<!-- ngRepeat: member in members -->
<!-- ngRepeat: member in members -->
<!-- end ngRepeat: member in members --> (x30)
I also tried this:
<tr ng-repeat-start="member in members">
<th ng-repeat="project in member.projects" ng-repeat-end ng-if="member.projects" >
{{project.name}}
</th >
</tr>
But then I have the error: Unterminated attribute, found 'ng-repeat-start' but no matching 'ng-repeat-end' found.
Does anyone have an idea? Thanks in advance.
Updated answer:
Get only the 'projects' arrays into a new array, like so:
$scope.projectsOnly = [];//empty array
$scope.members.forEach(function(item){
$scope.projectsOnly.push(item.projects);
})
and then loop only into that array, like so:
<tr>
<th ng-repeat="project in projectsOnly " >
{{project.name}}
</th >
</tr>
Hope helps, good luck.
You should simply do this with nested ng-repeat
<tr ng-repeat="member in members">
<th ng-repeat="project in member.projects" ng-if="member.projects">
{{project.get('name')}}
</th>
</tr>
you don't need the ng-if, and you shouldn't separate the ng-repeat start/end. Just use two ng-repeats, one inside the other:
<div ng-app="app">
<table ng-controller="TestController">
<tr ng-repeat="member in members">
<th ng-repeat="project in member.projects">
{{project.name}}
</th>
</tr>
</table>
</div>
and your controller can look like this:
var app = angular.module('app', []);
app.controller('TestController', function($scope) {
$scope.members = [{
projects: [{
name: '1'
}, {
name: '2'
}]
}, {
projects: [{
name: '3'
}, {
name: '4'
}]
}];
});

How to collect data for the rows that have been selected in AngularJS?

I am receiving a list of data from server and has displayed that in table format using ng-repeat along with checkbox in each row. My requirement is to pass the selected rows back to server upon clicking a removeUserData button. Am facing issue to get it done, help would be appreciated.
<table border="2" border-color=black>
<tr data-ng-repeat="user in users">
<td><input type="checkbox"></td><td>{{user.id}}</td><td>{{user.country}}</td><td>{{user.name}}</td>
</tr>
</table><br>
<button data-ng-click="removeUserData()" data-ng-show="users.length">Remove User</button>
I'd suggest you to make use of a new property in users, something like removed, then when checkbox is checked it will be true, otherwise false.
See it working:
(function() {
angular
.module("app", [])
.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$scope'];
function MainCtrl($scope) {
$scope.removeUserData = removeUserData;
$scope.users = [
{
"id":1,
"country":"Italy",
"name":"Pavarotti"
},
{
"id":2,
"country":"French",
"name":"Some user"
}
];
function removeUserData() {
$scope.users = $scope.users.filter(function(user) {
return !user.removed;
})
}
}
})();
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
</head>
<body ng-controller="MainCtrl">
<table>
<tr ng-repeat="user in users">
<td>
<input type="checkbox" ng-model="user.removed">
</td>
<td ng-bind="user.id"></td>
<td ng-bind="user.country"></td>
<td ng-bind="user.name"></td>
</tr>
</table>
<div ng-if="users.length">
<hr>
<button ng-click="removeUserData()">Remove User</button>
</div>
</body>
</html>
your html
<div ng-app='myApp' ng-controller='myCtrl'>
<table border="2" border-color=black>
<tr data-ng-repeat="user in users">
<td>
<input type="checkbox" ng-change="storeIndexOfRow($index)">
</td>
<td>{{user.country}}</td>
<td>{{user.name}}</td>
</tr>
<button data-ng-click="removeUserData()" data-ng-show="users.length">Remove User</button>
Controller
var app = angular.module("myApp", []);
angular
.module('myApp')
.controller('myCtrl', ['$scope',
function ($scope) {
$scope.users = [
{ country: 'india', name: 'name1' },
{country: 'india2', name: 'name2'}
];
$scope.selectedIndex = [];
$scope.storeIndexOfRow = function (val) {
//write the logic for if checbox is checked or not
$scope.selectedIndex.push(val);
console.log($scope.selectedIndex);
};
$scope.removeUserData = function () {
angular.forEach($scope.selectedIndex, function (v, k) {
console.log($scope.users[v]);
});
};
}]);
One option is to use the ng-model to store a map that will decide for each row if it should be deleted or not,
The ng-model will bind the checkbox value to the given expression, in our case to a map.
for more information about ng-model see official documenation
the map will use user.id as a key, and will store a boolean value, based on the checkbox value.
lets call that map shouldDeleteUserMap.
Then we can filter your users array before sending it back to the server, based on shouldDeleteUserMap.
<table border="2" border-color=black>
<tr data-ng-repeat="user in users">
<td><input type="checkbox" ng-model='shouldDelteUserMap[user.id]' ></td><td>{{user.id}}</td><td>{{user.country}}</td><td>{{user.name}}</td>
</tr>
</table><br>
<button data-ng-click="removeUserData()" data-ng-show="users.length">Remove User</button>
and your controller, would look a bit like this:
angular.module('app',[])
.controller('myCtrl', function($scope){
$scope.shouldDelteUserMap = {};
$scope.users = [{
id: 1,
country: 'USA',
name: 'john'
},
{
id: 2,
country: 'Germany',
name: 'jane'
}];
$scope.removeUserData = function(){
var usersToRemove = $scope.users.filter( function(user){
return $scope.shouldDelteUserMap[user.id];
});
console.log(usersToRemove); // here comes your function that calls the server
}
});
and here is jsbin with an example:
http://jsbin.com/jisigiboha/edit?html,css,js,console,output

Filtering an array by number in AngularJS

I'm wondering if I can do something like the code below in AngularJS. The goal is to filter the array by all ids >= the search id. Can I do the search inline or does it have to be done as a custom filter in javascript?
<div style="margin-top:5px;margin-left:30px;">
<div>
<div>ID</div>
<span class="glyphicon glyphicon-search"></span>
<input type="text" ng-model="listFoodItems.searchid" />
</div>
<table class="table table-responsive">
<thead>
<tr>
<th>ID</th>
<th>Description</th>
<th>Discount</th>
</tr>
</thead>
<tfoot></tfoot>
<tbody>
<tr ng-repeat="row in listFoodItems.fullitemdescs | filter: EntryId >= searchid">
<td>{{row.EntryId}}</td>
<td>{{row.ItemDesc}}</td>
<td>{{row.ItemDisc}}</td>
</tr>
</tbody>
</table>
</div>
Best way make a custom filter like:
HTML
<div ng-app>
<div ng-controller="MyCtrl">
<input type="text" ng-model="searchid" />
<ul>
<li ng-repeat="person in people | filter:myFilter">
{{person.id}}
-
{{person.name}}
</li>
</ul>
</div>
</div>
JS
function MyCtrl($scope){
$scope.people = [
{id: 1, name: "Mark"},
{id: 2, name: "John"},
{id:3, name: "Joe"}
];
$scope.myFilter = function(item){
if(item.id >= $scope.searchid){
return item;
}
};
}
here its my fiddle with example: https://jsfiddle.net/javierif/mmoo3s8m/
First, create a function
$scope.lessThan = function(prop, val){
return function(item){
return item[prop] < val;
}
}
Next, in your ng-repeat
<tr ng-repeat="row in listFoodItems.fullitemdescs | filter: lessThan('EntryId', searchid)">
Original answer here:
https://stackoverflow.com/a/24081564/5141584

Highlight filtered rows in Angular JS

I have a array that needs to highlight duplicate values. This is what I have so far..
HTML
<tr ng-repeat="x in names | unique: 'names'" >
<td> {{ x }} </td>
</tr>
<style>
.duplicate{
background: yellow;
}
</style>
Controller:
angular.module('myApp', []).controller('nameCtrl', function($scope) {
$scope.names = [
'Bob',
'Dan',
'Philip',
'Philip',
];
});
EDIT: In the example above "Philip" would be highlighted and would have duplicate class
You can achieve it like this:
<div ng-app="MyApp">
<div ng-controller="MyController">
<table>
<tr ng-repeat="name in names | unique">
<td ng-class="{ 'duplicate': names.indexOf(name) !== names.lastIndexOf(name) }">{{name}}</td>
</tr>
</table>
</div>
</div>
This applies the duplicate class for elements where the indexOf returns a different value than the lastIndexOf. This can only happen for items that are in the array more than once.
JSFiddle available here.
If you don't want to filter out the duplicates, you can use this:
<div ng-app="MyApp">
<div ng-controller="MyController">
<table>
<tr ng-repeat="name in names track by $index">
<td ng-class="{ 'duplicate': names.indexOf(name) !== names.lastIndexOf(name) }">{{name}}</td>
</tr>
</table>
</div>
</div>
I think this should work.
<tr ng-repeat="(key, count) in names | count" >
<td ng-class="{your-highlight-class: count > 1 }"> {{ key }} </td>
</tr>
app.filter('count', function ( ) {
return function (collection) {
var result = {};
collection.forEach( function( elm ) {
if(!result[elm]) {
result[elm] = 0;
}
result[elm]++;
});
return result;
}
}]);

Categories