Sorting table rows using AngularJS - javascript

I'm just getting started with AngularJS and I'm trying to sort my table so that when a tableheader is clicked the rows are sorted per that table header.
Here is my plnkr link:
http://plnkr.co/edit/mbTq5865KKNzlvpJJy1l
Here is my relevant code:
Controller code:
$scope.setRepoSortOrder = function(order) {
if ($scope.repoSortOrder === order) {
if ($scope.repoSortOrder.charAt(0) === '+') {
order = order.replace('+', '-');
} else {
order = order.replace('-', '+');
}
}
$scope.repoSortOrder = order;
};
Table:
<table>
<thead>
<tr>
<th>Name</th>
<th>Stars</th>
<th>Language</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="repo in repos | orderBy:repoSortOrder">
<td>{{repo.name}}</td>
<td>{{repo.stargazers_count | number}}</td>
<td>{{repo.language}}</td>
</tr>
</tbody>
</table>
So when the name th is clicked the rows should be sorted by the names - same with stargazers_count and language. If a th is clicked again it should be sorted in the opposite order (if I click name first it's sorted by names in asc order - if I click it again it's sorted in desc order).
Finally: name and language should initially sort in asc order while stargazers_count should sort in desc order initially.
I've done this myself but I don't know if I'm doing it the best way possible. Since I'm not used to the "angular style" I would like to hear how people familiar with AngularJS would handle this. Please check out the plnkr link to see if you could improve it.
Any replies are appreciated!

One thing that can improve is to use a reverse flag of the orderBy filter like this:
$scope.repoSortOrder = "-stargazers_count";
$scope.isReverse = false;
$scope.setRepoSortOrder = function(order) {
$scope.isReverse = ($scope.repoSortOrder === order) ? !$scope.isReverse : false;
$scope.repoSortOrder = order;
};
Example Plunker: http://plnkr.co/edit/CM1BGkQc0AYbrraAPq8r?p=preview

Related

Highlight cell with highest value in ng-repeat with AngularJS 1.4.14

I am working on a dashboard and I'd like some help '_'.
I get a JSON from an API and I get an array of 5 element and each contain this structure (I simplified it but it is close to that).
{
"app_id": "id",
"app_name": "name",
"users_percentiles": {
"users_percentile_1": "3408",
"users_percentile_2": "2356",
"users_percentile_3": "988",
"users_percentile_4": "1099",
}
}
Then, I use a table to organize those elements in my dashboard.
<tbody ng-repeat="dash in dashboard">
<tr>
<td>{{dash.app_id}}</td>
<td>{{dash.app_name}}</td>
<td ng-repeat="percentile in dash.users_percentiles">
{{(percentile}}%
</td>
</tr>
</tbody>
I'd like for each ng-repeat highlight the highest value of percentile (if two are equals, then both should be highlighted).
I think i should add something like :
ng-class="{max : percentile == maxPercentile}"
and a function :
$scope.maxPercentile = -1;
angular.forEach(percentiles, function (percentile) {
if (percentile > $scope.maxPercentile) {
$scope.maxPercentile = percentile;
}
});
But I don't know really where to use this method.
I tried with a $watch but no matter how I tried I didn't get it to work...
<tbody ng-repeat="dash in dashboard">
<tr>
<td>{{dash.app_id}}</td>
<td ng-init = "maxpercentile = getMaxPercentile(dash.users_percentiles)">{{dash.app_name}}</td>
<td ng-class="{max : percentile === maxpercentile}"
ng-repeat="percentile in dash.users_percentiles">
{{percentile}}%
</td>
</tr>
</tbody>
$scope.getMaxPercentile = function(percentiles){
var maxPercentile = -1;
angular.forEach(percentiles, function (percentile) {
if (percentile > $scope.maxPercentile) {
maxPercentile = percentile;
}
});
return maxPercentile;
}

orderBy not working with pagination and filters

<table>
<thead>
<tr>
<th class="col-md-3" ng-click="sortDirection = !sortDirection">Created At</th>
</tr>
</thead>
<tbody>
<tr dir-paginate="food in foods | filter:foodFilter | itemsPerPage:pageSize | orderBy:'created_at_date'">
<td class="col-md-"> {{food.created_at_date}} </td>
</tbody>
</table>
<dir-pagination-controls
max-size= 7
boundary-links="true">
</dir-pagination-controls>
This is only a snippet of my code but its too large to put up. Everything is working except only some of the created_at_date is in order. When I click on a different filter to add in or remove data depending on that filter, only some of it is entered into the correct place. My main question is: is there someway to sort all of the dates properly while still allowing the everything else function as well? All help is welcome, Thanks
(function () {
"use strict";
App.controller('foodsController', ['$scope'],
function($scope) {
$scope.sortDirection = true;
In your controller you can add the method to order the array before you loop over them.
Assuming your foods array has an array of objects, each with a key of created_at_date and a value:
App.controller('foodsController', function($scope) {
$scope.foods = [{
created_at_date: 6791234
}, {
created_at_date: 9837245
}, {
created_at_date: 1234755
}];
// create a method exposed to the scope for your template.
$scope.orderBy = function(key, array) {
// now you've received the array, you can sort it on the key in question.
var sorted = array.sort(function(a, b) {
return a[key] - b[key];
});
return sorted;
}
});
Now on your template, you have a method available to sort your values for you:
<table>
<thead>
<tr>
<th class="col-md-3" ng-click="sortDirection = !sortDirection">Created At</th>
</tr>
</thead>
<tbody>
<tr dir-paginate="food in orderBy('created_at_date', foods) | filter:foodFilter | itemsPerPage:pageSize">
<td class="col-md-"> {{food.created_at_date}} </td>
</tr>
</tbody>
</table>
The orderBy method which we've created on your controller returns an array, but it's just sorted by the key that's sent in as the first argument of the function. The second argument is the original array you're trying to sort.
At least this way you can check if you remove all your other filters to see if it's ordered correctly, if then after you add them back in it changes it's because those filters are also changing the order.

How pick up checkbox result in post method angularjs

I have a table that sets out a list of rules. When the checkboxes are clicked, I need them to take this "true" value and post to an API endpoint. This has been set up, but what I am getting back is that "associated_rule" is undefined.
I have tried setting $scope.associated_rule.selected = true; in my controller, but this still doesn't define the variable and throws up the same error in the console.
Here is my HTML form:
<form name="rules_form" method="post" ng-submit="attach()">
<table class="table table-striped table-hover" ng-model="associated_rules">
<thead>
<th>Rule Types:</th>
<th>Description:</th>
<th>Start Time:</th>
<th>End Time:</th>
<th>Apply Rule to Vehicle:</th>
</thead>
<tr ng-repeat="associated_rule in associated_rules">
<td>#{{ associated_rule.resource_ids.accounts }}</td>
<td>#{{ associated_rule.description }}</td>
<td>#{{ associated_rule.start_time }}</td>
<td>#{{ associated_rule.end_time }}</td>
<td><input type="checkbox" ng-model="associated_rule.selected" aria-label="rule"></td>
</tr>
</table>
<button class="btn btn-primary" ng-click="attach()">Attach</button>
</form>
My Controller event:
$scope.attach = function () {
$scope.associated_rule.selected = true;
var i;
for (i = 0; i < $scope.associated_rule.selected.length; i++) {
//need to create a loop where the true value is picked up and then I can send the data using a POST method. But I'm stuck on this.
}
console.log(the result of the event);
};
For now, I just want the results to console.log, so I can see that the event is creating the loop and displaying the results. After that, I should be OK.
Any help would be much appreciated.
I have fixed this by defining the $scope.rule as an empty array and setting the $scope.rule.selected to "false" by default.
Great! step one! But the checkboxes are ALL selecting when you click A checkbox - think this may be the ng-repeat causing me a pain in the backside.
$scope.rule = [];
$scope.rule.selected = false;
So, how do I ensure that only the checkboxes set that I select and not all at once?
Fixed this too; as the above was just making the entire array selected; as i wasn't drilling down into the array. This did it:
ng-model="rules.selected[associated_rule.id]"
by modelling the a rule within that defined array, it shows up when testing. Brill. :)
By mistake you are changing the value of your check box on clicking the button:
$scope.associated_rule.selected = true;
This will give the current value, selected or not selected
$log.log($scope.associated_rule.selected);

How to sort JSON data in ng-repeat?

I am getting a server response and binding these data to view using ng-repeat. Now I want to sort these data by priceList and name. I am able to sort name using orderBy, but not with priceList. I want to sort the products based on priceList. Sorting with name will change the order of list while sorting by priceList will effect only the order of products of each category. It will effect the order of displayed category. Please help me resolve this.
My code:
<div ng-controller="Ctrl">
<pre>Sorting predicate = {{predicate}};</pre>
<hr/>
<table class="friend">
<tr>
<th>Name
</th>
<th><a href="" ng-click="predicate = 'priceList'>price</a></th>
</tr>
</table>
<div ng-repeat="data in _JSON[0].categories | orderBy:predicate">
<div ng-repeat="vals in data.itemTypeResults |orderBy:'partTerminologyName'" id="{{vals.partTerminologyName}}">
`<h4 style="background-color: gray">{{vals.partTerminologyName}} : Position :{{vals.position}}</h4>`<br>
<div ng-repeat="val in vals.products">
<b> Quantity:{{val[0].perCarQty}}</b><br>
<b> part:{{val[0].partNo}}</b><br>
<b>sku:{{val[0].sku}}</b><br>
<b> qtyInStock:{{val[0].qtyInStock}}</b><br>
<b> priceList:{{val[0].priceList}}</b><br>
<b>priceSave:{{val[0].priceSave}}</b><br>
<b> qtyDC:{{val[0].qtyDC}}</b><br>
<b> qtyNetwork:{{val[0].qtyNetwork}}</b><br>
<b> priceCore:{{val[0].priceCore}}</b><br>
</div>
</div>
</div>
JS:
$scope._JSON = [
{"categories":
[
{"id":14061,"name":"Drive Belts",
"itemTypeResults":[
{"partTerminologyName":"Serp. Belt",
"position":"Main Drive",
"products":{
"5060635":[
{"perCarQty":2,"partNo":"5060635",
"sku":"20060904","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":19.15,"priceSave":3.29,
"priceCore":10.0}
],
"635K6":[
{"perCarQty":9,"partNo":"635K6",
"sku":"10062449","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":18.15,"priceSave":3.21,"priceCore":10.0}
]
}
}
]
},
{"id":2610,"name":"Drive Belt Tensioners, Idlers, Pulleys & Components",
"itemTypeResults":[
{"partTerminologyName":"Drive Belt Tensioner Assembly",
"position":"N/A",
"products":{
"950489A":[
{"perCarQty":4,"partNo":"950489A",
"sku":"10150833","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":18.15,"priceSave":3.21,"priceCore":10.0
}
]
}},
{"partTerminologyName":"Drive Belt Idler Pulley","position":"N/A",
"products":{
"89161":[
{"perCarQty":1,"partNo":"89161",
"sku":"99995959","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":17.15,"priceSave":3.21,"priceCore":10.0}
],
"951373A":[
{"perCarQty":2,"partNo":"951373A","pla":"LTN",
"plaName":"Litens",
"sku":"10150926","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":18.15,"priceSave":3.21,"priceCore":10.0}
]
}
}
]
}
]
}
];
$scope.predicate = '';
Fiddle: Fiddle
You might need to define a very good sorter function, or sort your products before they are interpreted by ng-repeat. I've created sorter function using underscore.js (or lodash).
You can checkout the demo (or the updated demo). Products are first sorted by category and then sorted by price in every category.
<!-- in html -->
<button ng-click="sortFn=sortByPrice">Sort By Price</button>
<button ng-click="sortFn=doNotSort">Do not Sort</button>
...
<div ng-repeat="val in sortFn(vals.products)">
...
// in js
$scope.sortByPrice = function(products) {
return _.sortBy(products, function(product) {
return product.length > 0 ? product[0].priceList : 0;
});
};
$scope.doNotSort = function(products) {
return products;
};
$scope.sortFn = $scope.doNotSort; // do not sort by default
BTW: You are directly calling val[0], which is very dangerous, if the product does not contain any elements, your code will break. My code won't ;-)
Update 1
The author asks me for a more pure Angular way solution.
Here is my answer: I think my solution is exactly in Angular way. Usually you can implement a filter (similar to orderBy) which wraps my sortByPrice. Why I don't do that, because you have ng-click to switch your order filter. I'd rather put control logic into a controller, not as pieces into view. This will be harder to maintain, when your project keeps growing.
Update 2
Okay, to make the +50 worthy, here is the filter version you want, (typed with my brain compiler) Please check in fiddle
You need to organize the products in other estructure. For example:
$.each($scope._JSON[0].categories , function( i , e) {
$.each(e.itemTypeResults, function(sub_i,sub_e) {
$.each(sub_e.products, function(itemTypeResults_i,product) {
console.log(product);
var aProduct = new Object();
aProduct.priceList = product[0].priceList;
aProduct.name = e.name;
$scope.products.push(aProduct);
});
} )
});
The code is not very friendly but what i do is putt all the products in one array so they can be ordered by the price. You have the products inside categories so that's why angular is ordering by the price in each category.
Fiddle:
http://jsfiddle.net/7rL8fof6/1/
Hope it helps.
Your fiddle updated: http://jsfiddle.net/k5fkocby/2/
Basically:
1. Digested the complex json object into a flat list of objects:
var productsToShow = [];
for (var i=0; i < json[0].categories.length; i++){
var category = json[0].categories[i];
for (var j=0; j<category.itemTypeResults.length;j++){
var item = category.itemTypeResults[j];
var products = item.products;
for (var productIndex in products) {
var productItems = products[productIndex];
for (var k=0; k<productItems.length;k++){
var productItem = productItems[k];
// Additions:
productItem.categoryName = category.name;
productItem.partTerminologyName = item.partTerminologyName;
productItem.position = item.position;
productsToShow.push(productItem);
}
}
}
}
Show category title only when needed by:
ng-repeat="product in (sortedProducts = (productsToShow | orderBy:predicate))"
and
ng-show="sortedProducts[$index - 1].partTerminologyName != product.partTerminologyName"
you can sort from your database and get final JSON data..
db.categories.aggregate([{$group : {category : {your condition} }, price: {$sort : { price: 1 } },}}])

Sort table columns with angularjs in non-angular application

Ok, suppose I have a table
<table>
<tr>
<td>Name</td><td>Phone</td>
</tr>
<tr>
<td>John</td><td>2222222</td>
</tr>
<tr>
<td>Mark</td><td>3333333</td>
</tr>
<tr>
<td>Alice</td><td>1999999</td>
</tr>
</table>
That was rendered by non-angularjs app.
All the examples I see on the web suggest that I have a controller and a scope and all the angularjs stuff, but what if I want to deal with existing data?
One time I have this headache I've surrendered and re-wrote everything to be an angularjs app.
There is a major issue with: it's not indexed by the search engines. Or at least not by all of them (I know google is super-smart, but that's only google).
What if I want to use angularjs power and have this existing data? How do I bind those values of the table and make it sortable?
This is interesting and I think it is useful when we want to give old web application power of angularjs.
The following code is nothing more than my idea without careful consideration.
In Html
<div ng-app="myapp" ng-controller="myctrl">
<table>
<tr>
<td><a href ng-click="mgrtable.sort('name')">Name</href></td><td><a href ng-click="mgrtable.sort('phone')">Phone</a></td>
</tr>
<tr ng-init="mgrtable.add_row('John', '2222222')">
<td>{{mgrtable.rows[0].name}}</td><td>{{mgrtable.rows[0].phone}}</td>
</tr>
<tr ng-init="mgrtable.add_row('Mark', '3333333')">
<td>{{mgrtable.rows[1].name}}</td><td>{{mgrtable.rows[1].phone}}</td>
</tr>
<tr ng-init="mgrtable.add_row('Alice', '1999999')">
<td>{{mgrtable.rows[2].name}}</td><td>{{mgrtable.rows[2].phone}}</td>
</tr>
</table>
</div>
In javascript
angular.module('myapp', [])
.factory('ManagerTable', function(){
return function(){
this.rows = [];
var desc = {
name: true, phone: true
}
this.add_row = function(name, phone){
this.rows.push({name: name, phone: phone});
}
this.sort = function(key){
var _desc = desc[key];
desc[key] = !_desc;
this.rows.sort(function(a, b){
var vala = a[key],
valb = b[key];
if (_desc){
return vala > valb ? 1: -1;
}else{
return vala > valb ? -1: 1;
}
});
}
};
})
;
function myctrl($scope, ManagerTable){
$scope.mgrtable = new ManagerTable();
}
Demo jsfiddle is here.
When you want to add some feature about the table data, all you have to do is adding the function of ManagerTable. Although I know html is not clean, I think this way could keep the MVC principle and angular-way.
Please let me know what you think.

Categories

Resources