angular-ui-scroll with (key, value) ng-repeat syntax - angularjs

I am looking into performance improvements for a large table I am rendering and have come across angular-ui-scroll which I would like to try out.
In my table I am using the key\value accessor on my ng-repeat, e.g:
<tr ng-repeat="(key, value) in vm.stopTimes track by key">
<td class="timetable-detail-stop" layout="row" flex layout-align="start center">
{{ vm.expandedTimetable.stops[key].name }}
</td>
<td ng-repeat="departure in value.times track by $index">
{{departure.time}}
</td>
</tr>
Can I use the supported key\value syntax from ng-repeat with ui-scroll? I'm not so sure I can having read through the docs.
Has anyone done this using keyed objects\dictionaries?
Thanks

If we are talking about table rows virtualizing, then it may look like
<div style="height: 500px; overflow: auto;" ui-scroll-viewport>
<table>
<tr ui-scroll="item in vm.datasource">
<td class="timetable-detail-stop">
{{ vm.expandedTimetable.stops[item.key].name }}
</td>
<td ng-repeat="departure in item.value.times track by $index">
{{departure.time}}
</td>
</tr>
</table>
</div>
Where the datasource is an object with get method which returns (via success callback) an array of items based on index and count params:
this.datasource = {
get: (index, count, success) => {
let result = [];
for (let i = index; i <= index + count - 1; i++) {
const stopTimeKey = this.getStopTimeKeyByIndex(i);
result.push({
key: stopTimeKey,
value: this.stopTimes[stopTimeKey]
});
}
success(result);
}
}
Here I assume that we can get stopTimes key by index... The simplest implementation of getStopTimeKeyByIndex method may be
this.getStopTimeKeyByIndex = (index) => Object.keys(this.stopTimes)[index];

Related

How to display json data in two columns in a table using angular

I have data in my json it will be dynamic and I want to display the data in a table with two columns. I am trying but only the first two records are repeating. I don't see all the records in the table.. Any help?
http://plnkr.co/edit/Hnb7hkjA16XDbzRT8VAt?p=preview
This is my json : var data = '{
"output":{
"service-status":[
{
"service":"db",
"service-name":"Mongo DB"
},
{
"service":"license",
"service-name":"Smart License"
},
{
"service":"BRM",
"service-name":"Billing"
},
{
"service":"subscription",
"service-name":"subscription"
}
]
}
}';
my html code:
<table border="1px" width="100%" ng-repeat="data in serviceData" ng-if="$index % 2 == 0">
<tr>
<td>{{serviceData[index]["service-name"]}}</td>
</tr>
</table>
i want to display something like this
Mongo Db Smart License
Billing subscription
Transform your data in the controller:
var serviceDataView = function() {
var res = [];
for (var i = 0; i < $scope.serviceData.length; i+=2){
res.push({
col1: $scope.serviceData[i]['service-name'],
col2: $scope.serviceData[i+1] ? $scope.serviceData[i+1]['service-name'] : null
});
}
return res;
};
$scope.structuredData = serviceDataView();
So that it can be easily used in the view:
<table border="1px" width="100%">
<tr ng-repeat="data in structuredData">
<td>{{data.col1}}</td>
<td>{{data.col2}}</td>
</tr>
</table>
Plunker.
The iterator that uses ng-repeat is $index.
Others Iterator: https://docs.angularjs.org/api/ng/directive/ngRepeat
Replace your table for:
<table border="1px" width="100%" ng-repeat="data in serviceData">
<tr>
<td>{{serviceData[$index]["service"]}}</td>
<td>{{serviceData[$index]["service-name"]}}</td>
</tr>
</table>
As per my review, $index is not required at all for this issue. You can use filter for table ngRepeat.check this [link]http://plnkr.co/edit/Hnb7hkjA16XDbzRT8VAt?p=preview
html code here:
<table border="1px" width="100%" ng-repeat="data in serviceData |limitTo:2 ">
<tr>
<td>{{data["service-name"]}}</td>
<td>{{data.service}}</td>
</tr>

AngularJS - orderBy value when ng-repeat by key, value pair

I have counted the ocurrences of each item in a set of data with countBy function in lodash, and the result is this:
$scope.colors= {
"Orange": 3,
"Blue": 2,
"Pink": 1,
"Red": 1,
"Black": 2,
};
Now I would like to display the data and order it by its value. I've tried with
<div ng-controller="myCtrl" ng-app="myApp">
<ul>
<li ng-repeat="(key,value) in colors | orderBy:value">
{{key}} ({{value}})
</li>
</ul>
</div>
But the orderBy filter doesn't seem to do the trick (see the plunkr here)
Is there any way to do it with the current data form, or is it a better way to structure the data in order to achieve the desired result? Thanks in advance!
As the documentation says, orderBy expects an array as input, not an object. In general, although it's supported, I've never met a use-case where using an object with ng-repeat was a good idea.
Just transform your object into an array:
$scope.colors = [];
angular.forEach(occurrences, function(value, key) {
$scope.colors.push({
color: key,
count: value
});
});
and then use
<li ng-repeat="element in colors | orderBy:'count'">
{{ element.color }} ({{ element.count }})
</li>
See your updated plunkr.
After multiple tries, I found a way to iterate an object and have it ordered by the key.
<div ng-repeat="key1 in keys(obj1) | orderBy: key1">
I would instead use an array like so:
colors = [{color: 'red', value: 1}, {color: 'blue', value: 2}]
Then you can use
ng-repeat="color in colors | orderBy:'value'"
Plunkr
different approach - for
string from JsonConvert.SerializeObject(DataTable), so basically List<Dictionary<string, string>>
Example is combination of few solutions (sorry for no references)
In AngularJs controller:
vm.propertyName = '\u0022Spaced Column Name\u0022';
vm.reverse = true;
vm.sortBy = function (propertyName) {
vm.reverse = (vm.propertyName === '\u0022' + propertyName +'\u0022') ? !vm.reverse : false;
vm.propertyName = '\u0022' + propertyName + '\u0022';
};
vm.Data = JSON.parse(retValue.Data);
for (var i = 0; i < vm.Data.length; i++) {
var obj = {};
angular.forEach(vm.Data[i], function (value, key) {
obj[key] = value;
});
vm.DataArr.push(obj);
}
By ng-if="key != 'Id'" I'm hiding Id column with Guid
And then in html:
<table border="1">
<tr>
<td ng-repeat="(key, value) in vm.Data[0]" ng-if="key != 'Id'">
<button ng-click="vm.sortBy(key)">{{key}}</button>
<span ng-show="vm.propertyName == '\u0022'+key+'\u0022' && !vm.reverse" class="fa fa-arrow-up"></span>
<span ng-show="vm.propertyName == '\u0022'+key+'\u0022' && vm.reverse" class="fa fa-arrow-down"></span>
</td>
</tr>
<tr ng-repeat="row in vm.DataArr |orderBy:vm.propertyName:vm.reverse track by $index" ng-init="rowInd0 = $index">
<td ng-repeat="(key, value) in vm.Data[0]" ng-if="key != 'Id'">
<span>{{row[key]}}</span>
</td>
</tr>
</table>

How to change $index for two ngRepeats with same input but different filters?

I've simulated my problem here.
Looking into this html, you can see that I am doing two ng-repeats with the same array as input, but different filters to each one:
<div ng-app='Lists'>
<div ng-controller='listsController'>
<table>
<tbody>
<tr ng-repeat='item in listValues | filter : xxx track by $index' ng-click="update($index)">
<td>{{item.ref}}</td>
<td>{{item.others}}</td>
</tr>
</tbody>
</table><hr/>
<table>
<tbody>
<tr ng-repeat='item in listValues | filter : yyy track by $index' ng-click="update($index)">
<td>{{item.ref}}</td>
<td>{{item.others}}</td>
</tr>
</tbody>
</table><hr/>
<div>{{updateIndex}}</div>
</div>
</div>
And my js code:
var appModule = angular.module('Lists', []);
appModule.controller('listsController', function($scope) {
$scope.listValues = [
{'ref' : '1', 'others' : 'abc..'},
{'ref' : '2', 'others' : 'def..'},
{'ref' : '1', 'others' : 'ghi..'},
{'ref' : '2', 'others' : 'jkl..'}
];
$scope.xxx = function(a){
return a.ref == 1;
};
$scope.yyy = function(a){
return a.ref == 2;
};
$scope.update = function(i) {
$scope.updateIndex = i;
};
$scope.updateIndex = "none";
});
The problem I'm stuck is that the update(index) function needs to change the object in the correct index of the listValues array. But as you can see clicking in the object of the second table gives me the $index of the first table.
How to work around this situation? Thanks in advance.
Using the $index is doomed to fail, even if you iterate once. $index is the index of the current item in the filtered array. And that index is different from the index of the same element in the original, non-filtered array.
If you want to modify an item on click, don't pass its index as argument. Pass the item itself:
ng-click="update(item)"
Instead of filters use ng-if which allows you to track items by index.Index will give exact click even list has duplicate items
<body>
<div ng-app='Lists'>
<div ng-controller='listsController'>
<table>
<tbody>
<tr ng-repeat="item in listValues track by $index" ng-click="update($index)" ng-if="xxx(item)=='1'">
<td>{{item.ref}}</td>
<td>{{item.others}}</td>
</tr>
</tbody>
</table><hr/>
<table>
<tbody>
<tr ng-repeat='item in listValues track by $index' ng-click="update($index)" ng-if="item.ref=='2'">
<td>{{item.ref}}</td>
<td>{{item.others}}</td>
</tr>
</tbody>
</table><hr/>
<div>{{updateIndex}}</div>
</div>
</div>
</body>

How to display the data / array from the filter function to view inside expression?

This is what I have done
<div ng-app="myApp" ng-controller="PeopleCtrl">
<table border="1" ng-init="ageToShow=(people| underTwenty: 20).length >= 1">
And my underTwenty Function is as under
myApp.filter('underTwenty', function() {
return function(values, limit) {
var returnValue = [];
angular.forEach(values, function(val, ind) {
if (val.age < limit)
returnValue.push(val);
});
return returnValue;
};
I want to display the returnValue array in my View as expression like this
<div ng-app="myApp" ng-controller="PeopleCtrl">
<table border="1" ng-init="ageToShow=(people| underTwenty: 20).length >= 1">
{{ageToShow}}
I know this is not the proper way, but what it is..please help...
Plunker : http://plnkr.co/edit/ceeF5tXFlBqInW5J3bdq?p=preview
Find the updated code here:
<body>
<div ng-app="myApp" ng-controller="PeopleCtrl">
<table border="1" ng-init="ageToShow=(people| underTwenty: 20).length >= 1">
{{people| underTwenty: 20}}
<tr>
<th>Id</th>
<th>Name</th>
<th ng-if="!ageToShow">Age</th>
</tr>
<tr ng-repeat="person in people| underTwenty: 20" >
<td><span>{{person.id}}</span>
</td>
<td><span>{{person.name}}</span>
</td>
<td ng-if="!ageToShow"><span>{{person.age}}</span>
</td>
</tr>
</table>
</div>
</body>
</html>
Hope this is the one you want
In controller add new filter function:
$scope.criteriaMatch = function( criteria ) {
return function( person ) {
return person.age < criteria;
};
And in view change to:
<tr ng-repeat="person in people| filter:criteriaMatch(20)" >
And table show ony 2 rows with age less 20
http://plnkr.co/edit/PonnLcvrjEEHhceSqD0g?p=preview
Change this line in your code: <tr ng-repeat="person in people| filter: search" > to <tr ng-repeat="person in people| underTwenty: 20" >. Due to this, the table will contain only those items where the age is less then 20. (As far as I understood this is what you wanted.)
In your ng-repeat, pipe multiple filters to filter the people based on age as well.
<tr ng-repeat="person in people| filter: search | underTwenty: 20" >
I prefer your approach of keeping the filter separately instead of in the controller because, you can reuse that filter in other views as well if required. Else you may need to copy paste that code in multiple controllers.
But keeping the login in view is a bad idea. Instead In your controller, you can create that variable and check. This will remove the presentation from the logic.
It will become:
$scope.ageToShow = $filter('underTwenty')($scope.people, 20).length >= 1;
inject $filter into your controller.
DEMO

Restricting checkbox selection to a subset w/out using functions in ng-repeat

I want to restrict the number of records that may be selected in a list to a subset of the total records by disabling the remaining records once the max is reached. The list may be 1000s of records in length so performance is an issue. I'm worried my current solution (below/fiddle) will not scale. I've read several articles that warn against using functions (maxSelected() in this case) in ng-repeat for performance reasons, but not sure how I can accomplish this without them? Any suggestions would be greatly appreciated!
Here's the fiddle... http://jsfiddle.net/ALPEP/
HTML:
<div ng-controller="recordsCollectionController">
<table>
<tbody>
<tr
ng-repeat="record in records"
ng-class="{info:selected[record.id], warning:!selected[record.id] && maxSelected()}">
<td>
<input
type="checkbox"
ng-model="selected[record.id]"
ng-disabled="!selected[record.id] && maxSelected()"
id="{{record.id}}"/>
</td>
<td>
<label for="{{record.id}}">{{record.name}}</label>
</td>
</tr>
</tbody>
</table>
</div>
JS:
angular
.module('App',[])
.controller('recordsCollectionController',function($scope){
$scope.selected = {"1":true,"2":true,"3":true};
$scope.records = [
{"id":1,"name":"Homer"},
{"id":2,"name":"Marge"},
{"id":3,"name":"Bart"},
{"id":4,"name":"Lisa"},
{"id":5,"name":"Maggie"}
];
$scope.maxSelected = function(){
var count = 0;
for(x in $scope.selected){
if($scope.selected[x]) count++;
}
return (count===3) ? true : false;
};
});
Here's one option. Your original code had 2*N^2 compares per $digest. This has N compares per selection change. The basic change is to track the selected count and update it via ng-change, rather than counting it again every time it's needed.
<div ng-controller="recordsCollectionController">
<pre>selected = {{selected}}</pre>
<table class="table table-bordered table-striped">
<colgroup>
<col style="width:20px"/>
<col/>
</colgroup>
<tbody>
<tr
ng-repeat="record in records"
ng-class="{info:selected[record.id], warning:!selected[record.id] && selMax}">
<td>
<input
type="checkbox"
ng-model="selected[record.id]"
ng-disabled="!selected[record.id] && selMax"
ng-change="updateSelected()"
id="{{record.id}}"/>
</td>
<td>
<label for="{{record.id}}">
{{record.name}}
</label>
</td>
</tr>
</tbody>
</table>
</div>
angular
.module('App',[])
.controller('recordsCollectionController',function($scope){
$scope.selected = {"1":true,"2":true,"3":true};
$scope.selMax = true;
$scope.records = [
{"id":1,"name":"Homer"},
{"id":2,"name":"Marge"},
{"id":3,"name":"Bart"},
{"id":4,"name":"Lisa"},
{"id":5,"name":"Maggie"}
];
$scope.updateSelected = function(){
console.log("Ping!");
var count = 0;
for(x in $scope.selected){
if($scope.selected[x]) count++;
}
$scope.selMax = (count >= 3);
};
});

Resources