I am using Angular DataTables within my app. So far everything works well, except when I try to add custom sorting.
I have a set of data that returns a hyphen, "-" if there is no data. Here is my sorting function::
$.fn.dataTableExt.oSort['nullable-asc'] = function(a,b) {
if (a == '-')
return 1;
else if (b == '-')
return -1;
else
{
var ia = parseInt(a);
var ib = parseInt(b);
return (ia<ib) ? -1 : ((ia > ib) ? 1 : 0);
}
}
$.fn.dataTableExt.oSort['nullable-desc'] = function(a,b) {
console.log(a,b)
if (a == '-')
return 1;
else if (b == '-')
return -1;
else
{
var ia = parseInt(a);
var ib = parseInt(b);
return (ia>ib) ? -1 : ((ia < ib) ? 1 : 0);
}
}
..start controller...
vm.dtColumnDefs = [
DTColumnDefBuilder.newColumnDef(0).notSortable().withClass('hidden-print')
DTColumnDefBuilder.newColumnDef(1).withClass('td-fieldname'), //name
DTColumnDefBuilder.newColumnDef(2), //fieldType
DTColumnDefBuilder.newColumnDef(3).withOption('type', 'html-num-fmt'),
DTColumnDefBuilder.newColumnDef(4).notSortable(), //distribution
DTColumnDefBuilder.newColumnDef(5).withOption('type', 'html-num-fmt'), //cardinality
DTColumnDefBuilder.newColumnDef(6).withOption('type', 'nullable'), //Min
DTColumnDefBuilder.newColumnDef(7).withOption('type', 'nullable') //Max
];
...other angular code.....
This works just fine when tried on a standard table, but it doesn't play nice with the Angular app.
When I click on the heading of the table to sort - nothing happens. console.log above yields blank values. Is there a way to use custom sorting with the Angular directive?
Related
I inherited a codebase aka "legacy code" and I am very careful not to break anything. I stumbled upon an error while auto-formatting.
The purpose of the function is to sort a chart. I commented the portion I do not understand:
function sortChartBy(field,dir=1) {
if(g_sortMethod != field){
g_sortDir = 1;
}
g_sortMethod = field;
g_sortDir = g_sortDir * -1 * dir;
var ranks = g_chart["ranks"];
var sortMethod = g_sortMethod;
var sortDir = g_sortDir;
var sortFunc = undefined;
if(field == "release_date"){
sortFunc = function(a,b){
var aVal = a[sortMethod];
var bVal = b[sortMethod];
if(aVal == "1970-01-01") aVal = "9999--99-99";
if(bVal == "1970-01-01") bVal = "9999-99-99";
if(aVal < bVal) return -1 * sortDir;
if(aVal > bVal) return 1 * sortDir;
return 0;
};
}else{
sortFunc = function(a,b){
var aVal = a[sortMethod];
var bVal = b[sortMethod];
// ###### QUESTION HERE ########################################
if (aVal == -1 && bVal !=- -1) return 1;
// 'bVal !=- -1' What does that mean? Why '-' directly after '!='
if (aVal != -1 && bVal == -1) return -1;
if (aVal < bVal) return -1 * sortDir;
if (aVal > bVal) return 1 * sortDir;
return 0;
};
}
ranks.sort(sortFunc);
renderChart();
}
I use IntelliJ as an IDE. When I use auto format the '-' minus character after the '!=' moves one character to the right showing the line as
if (aVal == -1 && bVal != --1) return 1; // Invalid left hand side in prefix expression
Now Intellij shows an error stating Invalid left hand side in prefix expression and marking the '1' with the red error underline.
I don't understand why there are two minuses seperated with a space !=- -1 in the original code and why Intellij formats this into an error. The code seems to work fine in the original code. I have never seen !=- in JavaScript. To me this does not seem to be valid.
Please explain why the line if (aVal == -1 && bVal !=- -1) return 1; seems to work fine, but then gets autoformatted into an error.
What should the correct code look like?
Please explain why the line if (aVal == -1 && bVal !=- -1) return 1; seems to work fine, but then gets autoformatted into an error.
The formatter is indeed breaking that code, but I suspect it was already broken. :-) But the formatter is violating a fundamental principle: It's chaning the meaning of the code (in this case, from valid-but-probably-wrong syntax to invalid syntax :-D ).
Thanks to Mellet mentioned running it through a different formatter, my afternoon-sleepy brain understands why the original parses: It's the unary - operator being used twice.
So the code is != - -1 which is != -(-1) which is != 1.
But it's probably a typo and the check was probably supposed to be against -1, not 1.
I'm populating a few drop downs from some JSON, it works as expected at the moment but i'm trying to sort the values in the drop downs. For all my drop downs it works fine, except for one of them which has numbers in, in which case it lists it 1, 10, 12, 2.
Any ideas how i can keep it sorting alphabetically for everything else but get the sort to work with the numeric values too?
Here's my JS (this replicates for each field - probably should try to find a way to make this reuseable):
var populateGenres = _.map(data.genres, function (val) {
return '<option>' + val + '</option>';
}).join();
var target = '#genreField';
$('#genreField').html(populateGenres);
reArrangeSelect(target);
Here's the sort JS:
function reArrangeSelect(target) {
$(target).html($(target + " option").sort(function(a, b) {
return a.text == b.text ? 0 : a.text < b.text ? -1 : 1
}))
}
My HTML is in this format:
<td>
<select id="genreField" class="form-control"></select>
</td>
<td>
<select id="authorField" class="form-control"></select>
</td>
function reArrangeSelect(target) {
$(target).html($(target + " option").sort(function(a, b) {
// u can use that 'getValue' function
// for "a.text == 'some string'" it will return 'some string' (string type),
// for "a.text == '10'" it will return 10 (number type)
var aVal = getValue(a.text);
var bVal = getValue(b.text);
return aVal == bVal ? 0 : aVal < bVal ? -1 : 1;
}));
}
function getValue(val) {
var asNumber = parseFloat(val);
return isNaN(asNumber) ? val : asNumber;
}
You can sort data.genres before running it through the _.map functions:
data.genres.sort(function (a, b) {
a = parseInt(a, 10);
b = parseInt(b, 10);
if(a > b) {
return 1;
} else if(a < b) {
return -1;
} else {
return 0;
}
});
Once you sort the data then run your _.map snippet:
var populateGenres = _.map(data.genres, function (val) {
return '<option>' + val + '</option>';
}).join();
All of your options should now be sorted before you append them to the <select>.
DEMO
I am using jquery datatable to display data. I display '--' when there is no data. Currently when the table sorts the data all the '--' comes in the beginning and the order looks like below:
--
--
10
20
400
800
But I need to make '--' to be displayed last when sorted in ascending order and should look something like below:
10
20
400
800
--
--
Please let me know how can we get this behavior in jquery datatable?
you can use an exstension
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
"myorder-pre": function (a) {
},
"myorder-asc": function (a, b) {
if(a == '--' && b != '--')
return 1;
else if(b == '--' && a != '--')
return -1;
else if(b == '--'&& a == '--')
return 0;
else
{
a = parseFloat(a);
b = parseFloat(b);
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
}
},
"myorder-desc": function (a, b) {
if(a == '--' && b != '--')
return -1;
else if(b == '--' && a != '--')
return 1;
else if(b == '--'&& a == '--')
return 0;
else
{
a = parseFloat(a);
b = parseFloat(b);
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
}
}
});
myorder-pre is used before all the order call.
myorder-asc when you order asc. Return number negative if a minor b, positive if a major b, 0 if equal.
Desc work adverse
then in the definition of columns of datatable, use
"aoColumnDefs": [{ "sType": 'myorder'}]
You can make use of the following code :
$('#example').dataTable( {
"aaSorting": [[ 4, "desc" ]]
} );
For the reference
I'm sorting an object array that has a primary contact name, among other things. Sometimes this has a blank value and when I use the function below it sorts it all correctly, but all the blanks go at the top of the list instead of the bottom. I thought that adding the condition shown below would work, but it does not.
this.comparePrimaryContactName = function (a, b)
{
if(a.PrimaryContactName == "") return -1;
return a.PrimaryContactName > b.PrimaryContactName ? 1 : -1;
}
What am I missing?
I usually use something like this:
this.comparePrimaryContactName = function(a, b) {
a = a.PrimaryContactName || '';
b = b.PrimaryContactName || '';
if(a.length == 0 && b.length == 0)
return 0;
else if(a.length == 0)
return 1;
else if(b.length == 0)
return -1;
else if(a > b)
return 1;
else if(a < b)
return -1;
return 0;
}
Comparison functions must be reflective, transitive, and anti-symmetric. Your function does not satisfy these criteria. For example, if two blank entries are compared with each other, you must return 0, not -1.
this.comparePrimaryContactName = function (a, b)
{
var aName = a.PrimaryContactName;
var bName = b.PrimaryContactName;
return aName === bName ? 0 :
aName.length===0 ? -1 :
bName.length===0 ? 1 :
aName > bName ? 1 : -1;
}
Return 1 instead of -1 for blanks.
this.comparePrimaryContactName = function (a, b) {
if (a.PrimaryContactName == b.PrimaryContactName)
return 0;
if(a.PrimaryContactName == "") return 1;
return a.PrimaryContactName > b.PrimaryContactName ? 1 : -1;
}
Your sort function should return 0 if the two are equal, -1 if a comes before b, and 1 if a comes after b.
See the MDN sort doco for more information.
I'm working with an array of objects in Javascript and need to sort them by date and time. Here's the setup:
place
title
date (optional)
time (optional)
Conceptually, the application allows users to create a list of places they're planning to go. The array of events is manually ordered at first, and users can optionally add date and time values to places. I'm providing an button to sort by date...places with null dates need to be placed at the bottom of the list.
Right now, it's behaving differently across browsers. Here's the code (assume I have a handle on the _places array and the _list object):
var _orderByDate = function (e) {
YUE.preventDefault(e); // yui
_places.sort(function (a, b) {
var dateA = new Date(a.date),
dateB = new Date(b.date);
if ((!dateA === null) && (dateB === null)) return 0; //they're both null and equal
else if ((dateA === null) && (dateB != null)) return -1; //move a downwards
else if ((dateA != null) && (dateB === null)) return 1; //move b downwards
else if ((dateA == dateB)) return (a.time > b.time) ? 1 : ((b.time > a.time) ? -1 : 0);
else return (dateA > dateB) ? 1 : ((dateB > dateA) ? -1 : 0);
});
_list.updatePlaces(_places);
}
If you recognize the sort code above, that's because I got the basics from another post, but I felt this one deserved it's own since it deals with dates...the other was just dealing with null values and text.
Anyway, in Chrome, the list seems to sort in a random order, and it keeps sorting differently every time I execute the _orderByDate function. In Safari, it sorts mostly correct the first time, but puts one null date place at the top of the list. In Firefox, nothing happens at all.
I'm a bit of a beginner, and I don't have a CS background at all, so I'm not adept at the basics like arrays, dates, times etc...and my debugging skills are limited to the Firebug console. No errors are being reported, so I really have no idea what's going wrong.
One thing to note, if I eliminate the date type from the function so it sorts the items as strings, it works correctly...but that means 1/10/2011 would sort before 1/9/2011, so I think I need the date type in there.
Any ideas what's going wrong? Is there a smarter way to do what I'm trying to do?
EDIT: Adding log values
First sort (Chrome):
08/01/2010
null
null
08/03/2010
null
null
null
null
7/01/2010
null
null
null
Second sort (Chrome):
08/01/2010
null
null
null
07/01/2010
null
null
null
null
null
8/03/2010
null
null
[See it in action]
_places.sort(function (a, b) {
var dateA = new Date(a.date + a.time), // merge the date & time
dateB = new Date(b.date + b.time); // depending on the format
if (!a.date && b.date) return 1;
else if (a.date && !b.date) return -1;
else if (dateA === dateB) return 0;
else return (dateA > dateB) ? 1 : (dateB > dateA ? -1 : 0);
});
You can simplify the sorting algorithm by a great deal if you pre-process your array so that it will have a numeric representation of the column you want to sort by.
Add a column to the table that contains the UTC equivalent of the dates for instance. Then you can safely sort the array by the UTC property, but you will still display the string value.
for (var idx in _places)
_places[idx].UTC = _places[idx].date ? new Date(_places[idx].date).UTC() : 0;
_places.sort(function(a, b)
{
return a.UTC > b.UTC ? 1 : a.UTC < b.UTC ? -1 : 0;
});
If you don't want to use the Date object (pre-1970 dates):
for (var idx in _places)
{
var row = _places[idx];
if (!row.date)
{
row.sortable = 0;
continue;
}
var date = row.date.split('/');
row.sortable = 10000 * parseInt(date[2]) + 100 * parseInt(date[0]) + parseInt(date[1]); // year, month, day
}
_places.sort(function(a, b)
{
return a.sortable > b.sortable ? 1 : a.sortable < b.sortable ? -1 : 0;
});
Of course this assumes that your dates will always have the same M/D/Y format.
Here's the above algorithm in action: http://jsfiddle.net/krNnn/
Ok, we got busy and built a rather complex function that works across all browsers. Some of the requirements we had were pretty special (dates in the distant past, need to sort null dates at the bottom, subsort by time). Here's what we did:
var _orderByDate = function(e) {
YUE.preventDefault(e);
_places.sort(function(a,b) {
var Ay, Am, Ad, By, Bm, Bd;
var Ah, Am, Bh, Bm;
var dateA = a.date.split("/");
if( !dateA.length || dateA.length != 3 || isNaN(dateA[0]) ||
isNaN(dateA[1]) || isNaN(dateA[2]) ) {
dateA = -1;
} else {
Ay = parseInt(dateA[2]);
Am = parseInt(dateA[0]);
Ad = parseInt(dateA[1]);
}
var dateB = b.date.split("/");
if( !dateB.length || dateB.length != 3 || isNaN(dateB[0]) ||
isNaN(dateB[1]) || isNaN(dateB[2]) ) {
dateB = -1;
} else {
By = parseInt(dateB[2]);
Bm = parseInt(dateB[0]);
Bd = parseInt(dateB[1]);
}
// null checks
if(dateA == -1 && dateB == -1) return 0;
if(dateA == -1 && dateB != -1) return 1;
if(dateA != -1 && dateB == -1) return -1;
// year check
if(Ay > By) return 1;
if(By > Ay) return -1;
// month check
if(Am > Bm) return 1;
if(Bm > Am) return -1;
// day check
if(Ad > Bd) return 1;
if(Bd > Ad) return -1;
var timeA = a.time.split(":");
if( !timeA.length || timeA.length != 2 || isNaN(timeA[0]) ) {
timeA = -1;
} else {
if( timeA[1].match(/am/) ) {
Ah = parseInt(timeA[0]);
Am = parseInt(timeA[1].match(/\d+/));
} else if( timeA[1].match(/pm/) ) {
Ah = parseInt((timeA[0] * 1) + 12);
Am = parseInt(timeA[1].match(/\d+/));
}
}
var timeB = b.time.split(":");
if( !timeB.length || timeB.length != 2 || isNaN(timeB[0]) ) {
timeB = -1;
} else {
if( timeB[1].match(/am/) ) {
Bh = parseInt(timeB[0]);
Bm = parseInt(timeB[1].match(/\d+/));
} else if( timeB[1].match(/pm/) ) {
Bh = parseInt((timeB[0] * 1) + 12);
Bm = parseInt(timeB[1].match(/\d+/));
}
}
// null time checks
if(timeA == -1 && timeB == -1) return 0;
if(timeA == -1 && timeB != -1) return 1;
if(timeA != -1 && timeB == -1) return -1;
// hour check
if(Ah > Bh) return 1;
if(Bh > Ah) return -1;
// minute check
if(Am > Bm) return 1;
if(Bm > Am) return -1;
return 0;
} );
_list.updatePlaces(_places);
}