Closed ohaddahan closed 5 years ago
Ok, couple of updates @n-rodriguez .
I updated my version, was using the older version hence some of the docs were of sync for me :)
I noticed that Arel::Attributes::Attribute:in checks if the input argument is a Range
(also see the deprecation warning & that the current default behavior, :in
evaluates as SQL
in
which I assume very few people actually need, between
seems like the common need)
I managed to get it to work, it's ugly and definitely shouldn't be the official way, but will do as a proof of concept :)
I added the following formatter:
cond: :in , formatter: filter_in_range
def filter_in_range
lambda do |input|
def to_float(string)
Float(string)
rescue ArgumentError
string
end
a = input.split(',')
case a.size
when 2
Range.new(to_float(a[0]), to_float(a[1]))
when 1
Range.new(to_float(input),999999999);
else
input
end
end
end
And did the following monkey patching:
module AjaxDatatablesRails
module Datatable
class Column
module Search
private
def out_of_range?(search_value)
if search_value.is_a?(Range)
return true if search_value.first < SMALLEST_PQ_INTEGER
return true if search_value.last > LARGEST_PQ_INTEGER
else
Float(search_value) > LARGEST_PQ_INTEGER || Float(search_value) < SMALLEST_PQ_INTEGER
end
end
def integer?(string)
true if string.is_a?(Range) || Float(string)
rescue ArgumentError
false
end
end
end
end
end
On the frontend , I'm currently manually inserting 9.1,15.4
but I'll change it and have two separate fake inputs and an event listener to format it into the real input field.
For completeness the JS
part I added :
This function is needed to prevent sending multiple request while the user is still typing. Due to the asynchronous nature and race condition, the user might see the wrong results. (for example typing 100 but actually seeing results for 10)
var delayExec = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();
Sending the updated and formatted range to the DataTable filter.
function updateAjaxFilters(from, to, input) {
var spinner = document.getElementById('table_processing');
if (spinner !== null) {
spinner.setAttribute('status','loading');
spinner.innerHTML = '<i class="fa fa-spinner fa-spin fa-3x fa-fw"></i><h3>Loading...</h3>';
};
var fromVal = parseFloat(from.value);
var toVal = parseFloat(to.value);
var range = '';
if (isNaN(toVal) && isNaN(fromVal)) {
range = '';
} else if (isNaN(toVal) && !isNaN(fromVal)) {
range = fromVal;
} else if (!isNaN(toVal) && isNaN(fromVal)) {
range = 0 + "," + toVal;
} else if (!isNaN(toVal) && !isNaN(fromVal)) {
range = fromVal + "," + toVal;
}
input.value = range;
var prevInput = input.getAttribute('prev-val');
if (prevInput !== range) {
var event = new Event('change');
input.dispatchEvent(event);
input.setAttribute('prev-val',range);
}
}
Adding the event lister on all items the filter pair.
function addEvenListenerForAjaxTable(fromId, toId, inputId) {
var from = document.getElementById(fromId);
var to = document.getElementById(toId);
var input = document.getElementById(inputId);
if (from === null || to === null || input === null) {
console.log("WARN ::: addEvenListenerForAjaxTable : fromId = " + fromId + " , toId = " + toId + " inputId = " + inputId);
return;
}
[from,to].forEach( function(element) {
element.addEventListener('keyup',function(event) {
delayExec(function(){
updateAjaxFilters(from, to, input);
}, 500 );
});
});
}
And I add this listener to each column:
['column_name_1','column_name_2', ........].forEach( function(name) {
addEvenListenerForAjaxTable(name + '_filter_min', name + '_filter_max', name + '_filter');
});
The column_name_filter
input is the one ajax-datatables-rails
tracks (I made it invisible).
While column_name_filter_min/max
are user input fields.
And :
@view_columns ||= {
.....
.....
column_name: { source: 'ModelName.column_name' , cond: :in , formatter: filter_in_range },
....
....
Hi! Thanks for your feedback!
I see there is a date range filter, but is it possible to filter by min & max?
Yes but actually most of the job is done by https://github.com/vedmack/yadcf.
It seems that you start rewriting yadcf ^^
When using yadcf, it automagically creates 2 input field for ranges : the right handed one is the min and the left handed one is the max.
When setting values in the inputs fields, yadcf automagically sends both value in the same colum with a field delimiter : <min>-yadfc-delimiter-<max>
On the server side you only manage 1 column with one 1 value that you need to split to do the correct job but that's all :)
Didn't know yadcf
does that, also didn't want to integrate another gem.
If you delegate it to them, it's fine by me.
But since it's a rather common need, I think a little more documentation about it and maybe an example will help a lot.
Didn't know
yadcf
does that, also didn't want to integrate another gem.
yadcf is not really a gem but a JS lib. It's a plugin for JQuery Datatables that enhances a lot datatables.
But since it's a rather common need, I think a little more documentation about it and maybe an example will help a lot.
Working on it.
You can also use the code I pasted here as a work around if someone wants to use. I guess sometimes people will prefer a couple of methods instead of a adding a full library they have little visibility into.
I got a range filter to work like this:
in the datatable view I created a table above the datatable to input the range variables, then add some javascript to reload the datatable on change:
<table>
<tbody><tr>
<td>Minimum CWD:</td>
<td><input type="text" id="minCWD" name="minCWD"></td>
</tr>
<tr>
<td>Maximum CWD:</td>
<td><input type="text" id="maxCWD" name="maxCWD"></td>
</tr>
</tbody></table>
<script>
$(document).ready(function () {
// other options
var table = $('#example').DataTable()
$("#minCWD").change(function () {
table.ajax.reload();
});
$("#maxCWD").change(function() {
table.ajax.reload();
});
});
</script>
then to add the filter variables to the ajax call (in the coffee file):
ajax: {
url: $('#example').data('source'),
beforeSend: (xhr) => xhr.setRequestHeader('Content-Type', 'application/json'),
data: (d) ->
$.extend {}, d, 'minCWD': $('#minCWD').val(),
$.extend {}, d, 'maxCWD': $('#maxCWD').val()
}
// note: the beforeSend may not be required
then add a filter in the model_datatable.rb:
def get_raw_records
#YOUR TYPICAL SELECTION...note: I'm using AREL and joining schools with pstats
#now filter by your max min variables
if params['minCWD'].present?
schools = schools.where(pstats[:cwd_percent].gteq(params['minCWD']))
end
if params['maxCWD'].present?
schools = schools.where(pstats[:cwd_percent].lteq(params['maxCWD']))
end
return schools
end
My controller looks like this:
respond_to do |format|
format.html
format.json { render json: ExampleDatatable.new(params, view_context: view_context) }
end
working example here: https://schoolsparrow.com/arizona/schools
I see there is a date range filter, but is it possible to filter by min & max? From my understanding, only one condition per column is supported.