Angularjs - nested ng-repeat global index - angularjs

I have next template:
<div data-ng-repeat="supplier in order.Suppliers" data-ng-init="supplierIndex = $index">
<div data-ng-repeat="group in supplier.Groups">
{{something}}
</div>
</div>
And model:
$scope.order = {
Suppliers: [
{
Groups: [{ id: 'sss'}, {id: 'ddd'}]
},
{
Groups: [{ id: 'qqqq'}, {id: 'www'}, {id: 'xxx'}]
},
{
Groups: [{ id: 'ooo'}]
}
]
}
I need to display global group index, so output should be like this:
0
1
2
3
4
5
I know that I can use function that calculate index by passed group id at each place we need to display global group index, but how to accomplish this goal most gracefully?

You can use {{$index}} to show group index on your list.

You can merge groups like this in your controller.
$scope.mergedGroups = [];
for(var i=0; i < $scope.order.Suppliers.length; i++){
for(var k=0; k < $scope.order.Suppliers[i].Groups.length; k++){
$scope.mergedGroups.push($scope.order.Suppliers[i].Groups[k]);
}
}
then you can use a single ng-repeat and its done.
<div data-ng-repeat="group in mergedGroups" >
{{group}} {{$index}}
</div>

Your options:
$parent.$index
put supplier index inside supplier object
create component <supplier-info supplier="supplier" index="$index">

If done only in html:
<div data-ng-init="$parent.index = 0" data-ng-repeat="supplier in order.Suppliers">
<div data-ng-repeat="group in supplier.Groups">
<span data-ng-init="index=$parent.$parent.index;$parent.$parent.index = $parent.$parent.index + 1;">
{{index}}
</span>
</div>
</div>

Related

How can I process a list of data for display in the view in angular?

I have this list, and I would like to create a sum of each items' data portion, and display this in the view.
//this data
var list=[{
id:1,
data:[
{name:'item 1',qty:100,price:1},
{name:'item 2',qty:150,price:1.5}
]
},{
id:2,
data:[
{name:'item 1',qty:100,price:1},
{name:'item 2',qty:150,price:1.5}
]
}]
//this sum fun
function countmoney(l){
count_listmoney=0;
for(i=0;i<l.data.length;i++){
count_listmoney+=l.data[i].price*l.data[i].qty
}
return count_listmoney
}
HTML
<div id="viewtable">
<div ng-repeat="l in list">
<!--Is there an alternative better way?-->
<div>{{countmoney(l)}}</div>
</div>
</div>
Is there a better way than calling a function from the view like this?
Process your data first, then show it in the view:
// ideally in a service
var sums = []
for (var i=0; i < list.length; i++) {
sums.push(countmoney(list[i]))
}
// controller
$scope.sums = sums; // better: $scope.sums = dataService.getSums()
HTML
<div id="viewtable">
<div ng-repeat="sum in sums">
<div>{{sum}}</div>
</div>
</div>
Simple way
<div id="viewtable">
<div ng-repeat="l in list">
Sum : {{l.price*l.qty}}
</div>
</div>

Compare two lists in angularJs

I have two list that creat from two different json object:
<ul ng-repeat="a in user.data">
<li>
<md-checkbox>
{{ a.name }}
</md-checkbox>
</li>
</ul>
and :
<ul ng-repeat="x in job.data">
<li>
<md-checkbox>
{{ x.user.name }}
</md-checkbox>
</li>
</ul>
I have to compare two lists and remove {{ a.name }} that the same as {{ job.user.name }} . Comparing json objects that have different structure is hard. How can I compare this two list and remove repeated items?
You can use the filter filter ;) using a function instead of a string in the expression argument. Remember this is the syntax of filter
{{ filter_expression | filter : expression : comparator}}
The expression can be a string, an object or a function. Something like this will do
$scope.filterUnemployed = function(value) {
var jobs = $scope.job.data;
for (var i = 0; i < jobs.length; i++) {
// Search every object in the job.data array for a match.
// If found return false to remove this object from the results
if (jobs[i].user.name === value.name) {
return false;
}
}
// Otherwise return true to include it
return true;
}
And then apply the filter to your ng-repeat directive like this
<ul ng-repeat="a in user.data | filter:filterUnemployed">
<li>
<md-checkbox>
{{ a.name }}
</md-checkbox>
</li>
</ul>
Note that this will not modify your original collection but will result in a new copy beign displayed in your html since this is usually the desired effect.
Check the sample for a working demo
angular.module('app', [])
.controller('SampleCtrl', function($scope) {
$scope.user = {
data: [{
name: 'John'
}, {
name: 'Mary'
}, {
name: 'Peter'
}, {
name: 'Jack'
}, {
name: 'Richard'
}, {
name: 'Elizabeth'
}]
};
$scope.job = {
data: [{
jobTitle: 'CEO',
user: {
name: 'John'
}
}, {
jobTitle: 'CFO',
user: {
name: 'Mary'
}
}, {
jobTitle: 'Analist',
user: {
name: 'Jack'
}
}]
};
$scope.filterUnemployed = function(value) {
var jobs = $scope.job.data;
for (var i = 0; i < jobs.length; i++) {
if (jobs[i].user.name === value.name) {
return false;
}
}
return true;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="SampleCtrl">
<h1>All Users</h1>
<ul ng-repeat="a in user.data">
<li>
<md-checkbox>
{{ a.name }}
</md-checkbox>
</li>
</ul>
<h1>Unemployed users</h1>
<ul ng-repeat="a in user.data | filter:filterUnemployed">
<li>
<md-checkbox>
{{ a.name }}
</md-checkbox>
</li>
</ul>
<h1>Employed</h1>
<ul ng-repeat="x in job.data">
<li>
<md-checkbox>
{{ x.user.name }}
</md-checkbox>
</li>
</ul>
</div>
You can create a new array made from filtering one of your arrays :
var newArray = job.data.filter(function(jobData) {
return user.data.some(function(userData) {
return userData.name === jobData.user.name;
});
});
Something like this should help :
for(i=0; i<user.data.length;i++){
for(j=0; j<job.data.length;j++){
if(user.data[i].name === job.data[j].name){
user.date.splice(i,1);
}
}
}
I would appreciate some feedback, at least to know that my code helped you
You can use vanilla javascript, jQuery or library undescore.
Please check related links:
How can I merge two complex JSON objects with only unique or different values only showing in resultant array
How to merge two object values by keys
How to merge two arrays of JSON objects - removing duplicates and preserving order in Javascript/jQuery?
Merging two json objects in Java script?

Filtering and grouping with angularJS group count

Given the following data:
[
{"Id": 3, "Name":"Image1.jpg", "AssetType":0, "Grouping": {"GroupingId": 4, "Name":"Other"}},
{"Id": 7, "Name":"Document1.jpg", "AssetType":2, "Grouping": {"GroupingId": 4, "Name":"Other"}},
{"Id": 8, "Name":"Video1.jpg", "AssetType":1, "Grouping": {"GroupingId": 4, "Name":"Other"}},
{"Id": 6, "Name":"Image2.jpg", "AssetType":0, "Grouping": {"GroupingId": 1, "Name":"Facebook"}},
]
I wanted to list separate groups of assets types, so I used the following ng-repeat:
<div class="group" ng-repeat="asset in assets | filterBy : ['AssetType'] : 0 "
<b>{{ asset.Grouping.Name}}</b><br />
{{ asset.Name }}
</div>
This gets me the following:
Other
Image1.jpg
Facebook
Image2.jpg
Now, I wanted to group by the grouping name, to test if there was only one group for this asset type. If there was only one, I was not going to show the grouping name, so I added a group by to the ng-repeat statement:
<div class="group" ng-repeat="(key, value) in assets | filterBy : ['AssetType'] : 0 | groupBy : 'Grouping.Name' ">
<b>{{ key }}</b> - {{ numGroups(key) }}<br />
<div ng-repeat="asset in value ">
{{ asset.Name }}
</div>
</div>
But when I added a function to get the count of keys for this filtered group, I got something unexpected:
Other - 5
Image1.jpg
Facebook - 8
Image2.jpg
numGroups is defined like this:
$scope.numGroups = function (key) {
return Object.keys(key).length;
}
I was expecting the length of keys to be 2 (Other and Facebook) but instead it looks like it is iterating all the items of the array.
Is there any way to get the count of group keys after a filter has been applied?
There is a way!
Here's how the solution worked out:
$scope.numGroups = function (map) {
var count = 0;
angular.forEach(map, function () { count++; });
return count;
}
and the Html
<div class="group" ng-repeat="(key, value) in images = (assets | filterBy : ['AssetType'] : 0 | groupBy: 'Grouping.Name') ">
<div class="groupName" ng-hide="numGroups(images) == 1"><b>{{ key }}</b></div>
<div ng-repeat="asset in value">
{{ asset.Name }} - Brand {{ asset.Brand.Name }}
</div>
</div>
In your function 'numGroups' you are getting the length of the 'Grouping.Name' String, it could be fixed if you use this:
<div class="group" ng-repeat="(key, value) in grouped = (assets | groupBy : 'Grouping.Name') ">
<b>{{key}}</b> - {{numGroups(grouped)}} <br />
<div ng-repeat="asset in value">
{{ asset.Name}}
</div>
</div>
And a tricky way of your noumGroups function:
$scope.numGroups = function(map){
var count = 0;
angular.forEach(map, function(){
count++;
})
return count;
}

Can I get the value from the key from ng-repeat x in xx | unique x.key

I have a collection of items that represent a grouping by one property. I want to create a list of items grouped by the value of this property and in each list present the value of this group once and then all the items that belong to the group.
Something like the following
<div ng-repeat="items in itemcollection | unique: 'groupkey'">
<h3>{{items.groupkey}}</h3>
<div ng-repeat="item in items">
<label>{{item.name}}</label>
</div>
</div>
So if I have a itemscollection like the following:
{{ groupkey: 1; name: 'Ada'}, { groupkey: 1; name: 'Beda'}, {groupkey: 2; name: 'Ceda'}}
So after the generation of divs and labels the result should be
<div>
<h3>1</h3>
<div><label>Ada</label></div>
<div><label>Beda</label></div>
</div>
<div>
<h3>2</h3>
<div><label>Ceda</label></div>
</div>
Is it possible to create this or do I need to handle the creating of the elements to better construct the data to make this happen?
This filter does what you want : https://github.com/a8m/angular-filter#groupby
Yes. Its possible by the combination of orderBy and filter function in angularJS. The following code will work :
var app = angular.module("myApp", []);
app.controller("lists", function($scope) {
$scope.list = [{
groupkey: 1,
name: 'Ada'
}, {
groupkey: 1,
name: 'Beda'
}, {
groupkey: 2,
name: 'Ceda'
}];
});
<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.js"></script>
</head>
<body>
<div ng-controller="lists">
<div ng-repeat="items in list | orderBy:'groupkey'">
<h3>{{items.groupkey}}</h3>
<label>{{items.name}}</label>
</div>
</div>
</body>
</html>
Hope it works for you :)
Not tested but something like this I guess should work
<div ng-repeat="items in itemcollection | unique: 'groupkey'">
<h3>{{items.groupkey}}</h3>
<div ng-repeat="item in items | filter:{'groupkey': items.groupkey}:true">
<label>{{item.name}}</label>
</div>
</div>
If you make sure the itemcollection is sorted by groupkey you may display the groupkey header only when the groupkey differs from the previous groupkey. Then you achieve to only show the groupkey header once per group. Here is an example:
angular.module('myapp',[]).controller("thectrl", function($scope) {
$scope.itemcollection = [{ groupkey: 1, name: 'Ada'},
{ groupkey: 1, name: 'Beda'},
{groupkey: 2, name: 'Ceda'},
{groupkey: 2, name: 'D'}];
$scope.itemcollection.sort(function(a,b) { return a.groupkey-b.groupkey;});
});
The HTML:
<div ng-app="myapp" ng-controller="thectrl">
<div ng-repeat="item in itemcollection track by $index" >
<h3 ng-if="$index===0 || itemcollection[$index-1].groupkey!==item.groupkey">{{item.groupkey}}</h3>
<label>{{item.name}}</label>
</div>
</div>
And a working fiddle here:
https://jsfiddle.net/vuksyeyh/

How do I only show an element if nested ng-repeat is not empty?

I have a List of lists, created with a nested ng-repeat. Each outer ng-repeat contains a div with the label of its inner list (eg: "Group A"). I'm now trying to create a way to avoid showing this label if the inner list is empty due to filtering(Applied by an input searchtext)
Here is a plunker explaining my issue and my attempted solution : Plnkr
Having a 'heavy' function like isGroupEmpty seems extremely cumbersome - Is there any way to do this in a much simpler fashion? I was toying with the idea of moving the label inside the inner ng-repeat and having ng-show="$first" but it doesnt look great
I ended up with the following solution which worked perfectly. Plnkr
By setting a variable in the inner ng-repeat I was able to evaluate ng-show based on this variables length like so :
<input ng-model='searchText'/>
<span ng-show='filtered.length > 0'>
<ul>
<li ng-repeat='el in filtered = (model | filter:searchText)'>
<div>{{el.label}}</div>
</li>
</ul>
</span>
you could leverage ng-init, that way you'll call the filter only once:
<div ng-repeat='(key,group) in model'>
<div ng-init="filtered = (group | filter:filterFn)"></div>
<div ng-show="filtered.length !== 0">
<div>{{key}}</div>
<ul>
<li ng-repeat="el in filtered">
<div>{{el.label}}</div>
</li>
</ul>
</div>
</div>
usually it is not a good practice to use ng-init out of no where, but I guess it solves calling the filter twice.
Another way is to use the filter through javascript - you could inject $filter and retrieve 'filter' $filter('filter') in your controller, calling it with group as its first argument, the filterFn as its second, and store its result in your scope.
I used the following:
<ul>
<li ng-repeat="menuItem in menuItems"><span class="fa {{menuItem.icon}} fa-lg"></span>{{menuItem.itemName}}
<span ng-show='menuItem.subItems.length > 0'>
<ul>
<li ng-repeat="subItem in menuItem.subItems">{{subItem.itemName}}</li>
</ul>
</span>
</li>
checking if an array has a length of 0 is not an expensive operation. if you want to only show lists that have item, put a filter on the outer array that takes an array of arrays and returns only the arrays that have a length different than 0.
you can also hide the inner div if the array == false.
http://plnkr.co/edit/gist:3510140
http://plnkr.co/edit/Gr5uPnRDbRfUYq0ILhmG?p=preview
Your plunkr was pretty complicated and hard to weed through so I re-created what you wanted using a fiddle. The general idea behind my approach is to filter out the items from the array, not the sub array. And only do the filtered items when the text changes. So here's the markup:
<div ng-app="app">
<div ng-controller="ParentCtrl">
<input data-ng-model="filterText" data-ng-change="updateTypes()" />
<div data-ng-repeat="type in filteredTypes">
{{ type.name }}
<ul>
<li style="margin-left:20px;" data-ng-repeat="entry in type.entries">
- {{ entry.name }}
</li>
</ul>
</div>
</div>
</div>
And here's the code:
angular.module('app', [])
function ParentCtrl($scope){
$scope.filterText = "";
$scope.types = [
{ name: "type1", entries: [{ name: "name1"}, { name: "name2"}, { name: "name3"}]},
{ name: "type2", entries: [{ name: "name1"}, { name: "name3"}, { name: "name3"}]},
{ name: "type3", entries: [{ name: "name1"}, { name: "name2"}, { name: "name5"}]},
{ name: "type4", entries: [{ name: "name4"}, { name: "name2"}, { name: "name3"}]}
];
$scope.filteredTypes = [];
$scope.updateTypes = function(){
$scope.filteredTypes.length = 0;
for(var x = 0; x < $scope.types.length; x++){
if($scope.filterText === ""){
$scope.filteredTypes.push($scope.types[x]);
}
else{
var entries = [];
for(var y = 0; y < $scope.types[x].entries.length; y++){
if($scope.types[x].entries[y].name.indexOf($scope.filterText) !== -1){
entries.push($scope.types[x].entries[y]);
}
}
if(entries.length > 0){
$scope.filteredTypes.push({
name: $scope.types[x].name,
entries: entries
});
}
}
}
}
$scope.updateTypes();
}
Fiddle: http://jsfiddle.net/2hRws/
The reason I'm creating a new array and not using an actual filter to remove the results is that angular doesn't like creating dynamic arrays on the fly in filters. This is because it doesn't assign $$hashKey and things just don't line up correctly when dirty checking. I got the idea of how to do what you needed from this topic on the matter: https://groups.google.com/forum/#!topic/angular/IEIQok-YkpU
I have only slightly modified your list-widget.html, see it in action: plunkr
The idea is simple - use the same filter for ng-show:
<div ng-show="group | filter:searchText">{{ key }}</div>
The label will be visible only if there are some unfiltered items.
In my example I'm using searchText for filter because I'm not familiar with CoffeeScript.

Resources