Angularjs: How to sort array of arrays by nth value? - arrays

I have two groups of arrays.
First contains header fields =>
$scope.fields=[field0, field1, field2,...,field20];
Second has multiple arrays of data with respect to above field =>
$scope.data=[
[A,B,C,...,someVal20],
[Q,W,E,...,someVal20],
[R,T,Y,...,someVal20],
...
[B,N,M,...,someVal20]
];
Values of fields are shown in a drop down
<select ng-model="filteredOption" ng-options="option.label for option in fields"></select>
The same field values are used as table header.
<tr>
<th ng-repeat="f in fields">{{f.label}}</th>
</tr>
And the data as
<tr ng-repeat="d in data">
<td ng-repeat="i in d track by $index">{{i}}</td>
</tr>
Question is
How do I sort (ascending) the data based on the 'nth' field (which user selects from drop down)?
Its confusing because header and data are two distinct arrays, and data is not in key,value format.
Plunkr here

Column Header - ColumnName in you case it is field0
<th>
<a href="#" ng-click="sortType = 'field0'; sortReverse = !sortReverse">
field0
</a>
</th>
On Column header click it sets the properties of sortType and sortReverse
<tr ng-repeat="d in data | orderBy:sortType:sortReverse"">
<td ng-repeat="i in d track by $index">{{i}}</td>
</tr>
useful link for sorting https://scotch.io/tutorials/sort-and-filter-a-table-using-angular

Thanks to this answer, I've found working solution.
I tracked the selected field's position ($scope.sorter set to that $index). And used it as orderBy:sorter
Still, keeping this question open for others to suggest more acceptable solution.

Related

AngularJS incredibly slow performance when showing a table (1.6.5)

We are building an application to show a table with timetable data.
On the interface the user can set different filters.
I could use a data-grid which would speed up about everything.
I could use a table without grouping and use some sort of lazy fetching which would speed things up.
However we like the layout as is.
The consequence is that the watches are way over 2000 and we are experiencing bottlenecks. It is not that we show hundredths of rows.
How can we make this a bit more performant.
I tried track by, which didn't improve a thing. I tried bind-once but that didn't work either. (Honestly I have no clue how to make it work with key,value objects).
One performance trick might be changing the filters, move and chain them in the controller?
As you also can see, we re-use the same filters a lot, however this is necessary for the group by.
I also haven't seen any lazy-fetching mechanism which works with this kind of custom table / group by.
Hopefully you can help me to point me in the right direction, since I really kinda like the current layout.
The dataset is being displayed in a table and is grouped by date.
Example output:
hrefDateA | hrefDateB | hrefDateC | hrefDateD
DateA
RowA with columns
RowB with columns
RowC with columns
DateB
RowD with columns
RowE with columns
DateC
RowA with columns
RowB with columns
RowC with columns
....
<div ng-if="includeDesktopTemplate" ng-show="whateverdata.length > 0">
<div>
Jump to:
<a ng-href="#tableheader{{$index}}" ng-repeat="(key, value) in whateverdata | filter:filterA() | filter:filterB() | filter:filterC() | groupBy: 'someproperty'" class="someclass">
{{key}}
</a>
</div>
<hr />
<table>
<thead>
<tr>
<th class="timetablerow">HeaderA</th>
<th class="timetablerow">HeaderB</th>
<th class="timetablerow">HeaderC</th>
<th class="timetablerow">HeaderD</th>
<th class="timetablerow">HeaderE</th>
<th class="timetablerow">HeaderF</th>
</tr>
</thead>
<tbody ng-repeat="(key, value) in whateverdata | filter:filterA() | filter:filterB() | filter:filterC() | groupBy: 'someproperty'">
<tr>
<td colspan="6" class="desktoptablegroupby" id="tableheader{{$index}}">
{{key}}
</td>
</tr>
<tr>
<td colspan="6">
<hr class="redbackground" />
</td>
</tr>
<tr ng-repeat="row in value | filter:filterA() | filter:filterB() | filter:filterC()" ng-class-odd="'odd'" ng-class-even="'even'">
<td class="timetablerow">
{{row.propertyA}}
</td>
<td class="timetablerow">
{{row.propertyB}}
</td>
<td class="timetablerow">
{{row.propertyC}} - {{row.propertyD}}
</td>
<td class="timetablerow">
{{row.propertyD}}
</td>
<td class="timetablerow">
{{row.propertyE}}
</td>
<td class="timetablerow">
<div ng-show="{{row.propertyF}}">
<md-tooltip md-direction="{{tooltip.tipDirection}}">
{{row.propertyF}}
</md-tooltip>
<md-icon md-svg-src="~/Content/comment.svg">
</md-icon>
</div>
</td>
</tr>
</tbody>
</table>
<br /><br />
</div>
If I include the code below, watches can go from 3k to 6k
<div ng-show="{{row.propertyF}}">
<md-tooltip md-direction="{{tooltip.tipDirection}}">
{{row.propertyF}}
</md-tooltip>
<md-icon md-svg-src="~/Content/comment.svg">
</md-icon>
</div>
Regarding the code above. One column would show an icon with a tooltip which contains the value of an extra field of the dataset, only when the field contains data. But this also gives issues when the filters are being used (so redraw of screen), since other rows are showing the tooltip then, even when the value of the field of the specific row does not contain a value.(DOM/update/filter issue?)
This is a limitation by the way AngularJS handles change detection and rendering. There's really no easy solution - with emphasis on easy. What I've done on several occasions is use a technique often refered to as virtual-scroll/virtual-repeat. What it basically does is that it only renders the elements that can be seen in the viewport, but adds offsets to the top and bottom of the list/table to keep the scrollbar a constant size, regardless of how many elements are actually rendered. Then whenever you scroll, the elements that pop into view is seamlessly rendered before they become visible. This gives the illusion that it's a single long list/table, when it really only renders what is visible.
There are many libraries that implement this technique. I've personally got experience with the one called angular-vs-repeat but you should take a look at a few and evaluation which fits best your use case. I've also on one occasion implemented my own virtual scroll and it was certainly doable (my usecase in that scenario was that I needed virtual scroll to work both vertically and horizontally).

Can not sort table data alphabetically while using pagination in Angular.js

I have an issue.I am unable to sort table data in alphabetical order using Angular.js dir-paginate.Here i need to sort data from all page but in recent condition its sorting data for current page.I am explaining my code below.
<tbody id="detailsstockid">
<tr dir-paginate="cus in ($parent.labelResults=(listOfCustomerData | filter:searchProduct.rest_name)) | itemsPerPage:5 | orderBy:'rest_name' track by $index" current-page="currentPage">
<td>{{itemsPerPage *(currentPage-1)+$index+1}}</td>
<td>{{cus.rest_name}}</td>
<td>{{cus.quadrant_name}}</td>
<td>{{cus.city}}</td>
<td>{{cus.proviance}}</td>
<td>{{cus.person}}</td>
<td>{{cus.mobile}}</td>
<td>{{cus.url}}</td>
<td ng-if="cus.status==1">Active</td>
<td ng-if="cus.status==0">Inactive</td>
<td ng-if="cus.premium==1">Yes</td>
<td ng-if="cus.premium==0">No</td>
<td >
<a ui-sref="dashboard.customer.view">
<input type='button' class='btn btn-xs btn-green' value='Edit' ng-click="editProductViewData(cus.member_id);">
</a>
</td>
<td>
<a ui-sref="dashboard.customer.view">
<input type='button' class='btn btn-xs btn-red' value='Delete' ng-click="deleteProductInfoData(cus.member_id);" >
</a>
</td>
</tr>
</tbody>
In this case i can sort only the current page data but i need to sort data from the total array(i.e-listOfCustomerData).I am attaching my output below.
In this above pic the restaurant name is starting from c and this is page 1 pic.
For the page 2 the restaurant name is starting from b which is given above.Here i need all the restaurant should sort in alphabetically but not as per page.Please help me.
The order in which you chain filters is significant. Currently you're paginating before sorting:
<tr dir-paginate="... | itemsPerPage:5 | orderBy:'rest_name' ...">
...so the sort only affects the portion of the unsorted list that passed through the pagination filter.
In this case, all you need is do swap that order, and sort before paginating instead. Now the entire list will be sorted first, and then split into separate pages.
... | orderBy:'rest_name' | itemsPerPage:5 ...
Yes you can!
For sorting try this that will work for all the listOfCustomerData.
And for pagination you can combain this example instead of your version.

Why is my ng-if in td having unexpected results?

Maybe I am completely confused how td works as I haven't used tables in my code for yeeaars (strong div believer), or maybe I am confused on how ng-if is attempting to separate my data.
Either way, I am not getting my expected results, but there is otherwise nothing wrong with the code, I think my logic is just off.
Here is a working Plunker http://plnkr.co/edit/LoMEMBYc6kUodb9tcbfM?p=preview
<table>
<tr ng-repeat="z in waiverModalLinks">
<td ng-if="z.id=='color'" class="{{z.id}}">{{z.title}}</td>
<td ng-if="z.id==''" class="{{z.id}}">{{z.title}}</td>
</tr>
</table>
The ultimate end result is to have every other td a different color, however I realize that the current logic is going to put the colored td on the left and the non-colored td on the right. But that was the object I was working with before I realized I needed two separate columns, finding the need for a table in the first place.
But anyway, what is not working then is that.. well they are still stacked. I would expect to see the colored td on the left-hand side of the table and the non-colored td on the right-hand side of the table.
Where's the err in my logic?
Your data model seems to be the source of the confusion. You have a simple array, so each td will be in the same row. If you create a 2d array, you can make a new row for each inner array:
$scope.waiverModalLinks =[
[
{title:"1", link:"", id:"color"},
{title:"2", link:"", id:""}
],
[
{title:"3", link:"", id:"color"},
{title:"4", link:"", id:""}
]
];
And a simpler repeater:
<tr ng-repeat="row in waiverModalLinks">
<td ng-repeat="z in row" class="{{z.id}}">
{{z.title}}
</td>
</tr>
Plunkr: http://plnkr.co/edit/VeXBJcf3naI5bRttQ1Pt?p=preview
z.id is either equal to 'color', or an empty string, but can't be both at the same time. So every row will have at most one cell:
if z.id is equal to color, the generated dom will be
<tr ng-repeat="z in waiverModalLinks">
<td class="{{z.id}}">{{z.title}}</td>
<!-- removed second td -->
</tr>
if z.id is equal to ''', the generated dom will be
<tr ng-repeat="z in waiverModalLinks">
<!-- removed first td -->
<td class="{{z.id}}">{{z.title}}</td>
</tr>
You need two cells in every row: one blank and one filled, or vice-versa:
<table>
<tr ng-repeat="z in waiverModalLinks">
<td ng-if="z.id==''"></td>
<td class="{{z.id}}">{{z.title}}</td>
<td ng-if="z.id=='color'"></td>
</tr>
</table>

Creating and Assign variable value on template

In my view I have a table that will be filled dinamycally with data from server. This table should have at least 5 rows, which means that if my server doesn't return 5 sets of data, the table should be filled with 5 - (n_sets_returned). To make that happen I guess the easy way would be to define a variable and set it to the number of sets returned, but I can't make it. I tried as follow:
<tbody class="center-alignment xcompact" ng-init="cont = 0">
<tr ng-repeat="row in data">
<td>{{row.val1}}</td>
<td>{{row.val2}}</td>
<td>{{row.val3}}</td>
<td>{{row.val5}}</td>
{{cont = $index}}
</tr>
<tr ng-repeat="n in [] | range:(5-cont)">
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
</tbody>
The problem is I don't even know if this would work (probably not) because the empty rows appears before the filled ones, what means Angular obviously doesn't wait for server response to fill the empty rows.
I'm pretty new at Angular. Any help will be appreciated. Thanks.

Order a row or column in a table with angularJS

I have been manually ordering columns in my table using AngularJS. Now I have the problem that I need to order the rows in a very specific way (much like the columns). Instead of making this table huge in the Html, I would rather ask around to see if there is a way that I could perhaps define an array in the .js script and have the columns/rows order themselves based on those arrays.
I have seen plenty of examples of how to sort alphabetically or by index but none so far that show how to sort by a predefined array.
Here is the table I am working with:
<table>
<thead>
<tr>
<th>
<select ng-init="selectedRace='Human'" ng-model="selectedRace">
<option ng-repeat="(raceName, item) in races">{{raceName}}</option>
</select>
</th>
<!--<th ng-repeat="(statGroup, item) in race.Attributes" class="{{statGroup}}">{{statGroup}}</th>-->
<!--Going to manually type the column headers until I figure out how to sort via array or something-->
<th>Starting Attribuites</th>
<th>Hero Limit</th>
<th>Epic Limit</th>
<th>Veteran Limit</th>
</tr>
</thead>
<tbody ng-model="race.Attributes">
<tr ng-repeat="(statName, item) in race.Attributes['Starting Attributes']">
<td class="statsDes">{{statName}}</td><!--These are not in proper order! I need to sort this via array or something-->
<td class="Starting Attributes">{{race.Attributes['Starting Attributes'] | FilterSet:statName}}</td>
<td>{{race.Attributes['Hero Limit'] | FilterSet:statName}}</td>
<td>{{race.Attributes['Epic Limit'] | FilterSet:statName}}</td>
<td>{{race.Attributes['Veteran Limit'] | FilterSet:statName}}</td>
</tr>
</tbody>
</table>
</br>
Any ideas? Other than doing it manually?
No need to do anything manually when you have AngularJS at your disposal.
AngularJS offers the orderBy filter to order an array by expression (http://docs.angularjs.org/api/ng.filter:orderBy).
If you need some feature that is not supported by the default orderBy filter, you can write your own custom filter to manipulate (sort) the array to your liking.
You can read more about custom filters on http://docs.angularjs.org/tutorial/step_09.
Hope that helps!

Resources