Multiple Initialization After Destruction - javascript

I have a function that creates an instance of DataTables and for some reason it initializes n-times after destruction. I only noticed this because I add custom fields on init and they were multiplying. I could prevent that but that only deals with the symptom.
To clarify, after I "destroy: the instance and reinitialize it, to change the data source, if it's the second time it initializes twice. Three times if it's the 3rd time, etc.
I speculate that the table variable is part of the closure formed by the function because even if I set table = null the same thing happens.
How can I prevent this?
DataTables Function
/*Create a DataTable on tableElementID using pageUrl as the source*/
function ajaxLoadTable ( pageUrl, tableElementID ) {
window.table = $(tableElementID)
.on( 'init.dt', function () {
//The success function is used internally so it should NOT be overwritten, have to listen for this event instead
//Add our custom fields _length refers to an element generated datatables
if ( additionalElements.saveButton ) {
$(tableElementID + '_length').after('<div class="dataTables_filter"><button>Save All Edits</button></div>');
}
if ( additionalElements.selectState ) {
$(tableElementID + '_length').after('<div class="dataTables_filter"><label>Project State: <select name="projectState" style="width:auto;"><option>Select ...</option><option value="Active">Active</option><option value="Historical">Historical</option></select></label></div>');
}
if ( additionalElements.searchBox ) {
$(tableElementID + '_length').after('<div class="dataTables_filter"><label>Search:<input type="search" id="customSearch" style="width:auto;"></label></div>');
}
})
.DataTable({
"processing": true,
"serverSide": true,
"ajax":{
type: "POST",
url: pageUrl,
data: function ( additionalData ) {
$('.serverData').each( function( index, element ){
if( element.nodeName === "SELECT"){
additionalData[element.name.toUpperCase()] = element.options[element.selectedIndex].value;
return true; //return true is equivalent to continue for $.each
}
additionalData[element.name.toUpperCase()] = element.value;
});
},
dataType: "json"
},
"pageLength": 4,
"lengthMenu": [ 4, 8, 12, 16, 24 ],
"searchDelay": 1500,
"columnDefs":
{ "targets": 0,
"orderable": false,
"data": {
"_": "display"
}
}
});
}
Destruction Function
/*Load the selected project state*/
$('html').on( 'change' , '[name=projectState]' ,function(){
var currentState = $('option:selected', this).val();
$('#projectState').val(currentState);
//Remove the old table records and the datatables. Order matters, otherwise there is unsual behavior.
if( $.fn.DataTable.isDataTable( '#searchTable' ) ) {
window.table.destroy();
window.table = null;
}
$('.projectStateText').html( currentState );
//Get the new table records
ajaxLoadTable( *some undisclosed URL*, '#searchTable');
});

Related

Search within input and select

I have a datatable set up in an application where the rows contain input fields and I have sort and filter the tables based on the text values of the input fields. I managed to get sorting working correctly but I cannot for the life of me get search to work. I think the problem is related to the fact that the table is generated and populated dynamically by another JavaScript that runs prior to datatables being called on it.
Here is the JavaScript so far:
/* Create an array with the values of all the input boxes in a column */
$.fn.dataTable.ext.order["dom-text"] = function ( settings, col )
{
return this.api().column( col, {order:"index"} ).nodes().map( function ( td, i ) {
return $("input", td).val();
} );
}
/* Create an array with the values of all the input boxes in a column, parsed as numbers */
$.fn.dataTable.ext.order["dom-text-numeric"] = function ( settings, col )
{
return this.api().column( col, {order:"index"} ).nodes().map( function ( td, i ) {
return $("input", td).val() * 1;
} );
}
/* Create an array with the values of all the select options in a column */
$.fn.dataTable.ext.order["dom-select"] = function ( settings, col )
{
return this.api().column( col, {order:"index"} ).nodes().map( function ( td, i ) {
return $("select", td).val();
} );
}
$(document).ready(function() {
var table = $("#service_group0").DataTable({
"searching": true,
"ordering": true,
"columns": [
{ "orderDataType": "dom-text", type: \'html\' },
{ "orderDataType": "dom-select",type: \'html\' },
{ "orderDataType": "dom-text" , type: \'string\'},
{ "orderDataType": "dom-text", type: \'string\' },
{ "orderDataType": "dom-text", type: \'string\'},
{ "orderDataType": "dom-select" },
{ "orderDataType": "dom-select" },
{ "orderDataType": "dom-text-numeric"},
{ "orderDataType": "dom-text", type: \'date\'},
null,
{ "orderDataType": "dom-select" },
{ "orderDataType": "dom-text-numeric"},
{ "orderDataType": "dom-select" },
{ "orderDataType": "dom-text", type: \'string\' },
{ "orderDataType": "dom-text", type: \'string\' },
{ "orderDataType": "dom-text", type: \'date\'},
null,
null
],
initComplete: function () {
this.api().columns().every( function () {
var column = this;
if(column.index() == 5){
var select = $("<select id=\'strength_search\'></select>")
.appendTo( $(column.footer()).empty());
var strength_hidden = document.getElementById("strength_hidden").value;
select.append(strength_hidden);
}
else if(column.index() == 6){
var select = $("<select id=\'dose_search\'></select>")
.appendTo( $(column.footer()).empty());
var dose_hidden = document.getElementById("dose_hidden").value;
select.append(dose_hidden);
}
});
}
});
There is simply to much html to paste here so I have created a jsfiddle: http://jsfiddle.net/q715LncL/12/
If you go to the jsfiddle and type stuff into the empty text fields then go to the search box and try to filter based on something you typed in it always returns no results. How can I get it to filter on the changes made to the live inputs?
You need to add a custom filter function that checks the values of the input boxes, DataTables converts the HTML to strings and you can't recover the live value from them, so you would have to do something like this:
$.fn.dataTable.ext.search.push(
function( settings, data, dataIndex ) {
var searchTerm = $('.dataTables_filter input').val();
var $row = $('.dataTable tbody tr').eq(dataIndex);
if(!searchTerm) {
return true;
}
return $row.find('td input').filter(function() {
return ~$(this).val().toLowerCase().indexOf(searchTerm.toLowerCase())
}).size();
}
);
http://jsfiddle.net/q715LncL/14/
SOLUTION
You can use columnDefs to target a specific column using zero-based index in targets option and render to return selected value during searching (type === 'filter') or sorting (type === 'order').
columnDefs: [
{
targets: [0,1,2,3,4,5,6],
render: function(data, type, full, meta){
if(type === 'filter' || type === 'sort'){
var api = new $.fn.dataTable.Api(meta.settings);
var td = api.cell({row: meta.row, column: meta.col}).node();
data = $('select, input', td).val();
}
return data;
}
}
]
Also you need to invalidate cell data once data changes as shown below (according to this solution).
$('tbody select, tbody input', table.table().node()).on('change', function(){
table.row($(this).closest('tr')).invalidate();
});
DEMO
See this jsFiddle for code and demonstration.
NOTES
I have commented code that causes JavaScript errors because of the non-existing elements.

Call fnServerData in DataTables 1.10

So I'm looking at this example and am trying to make it work for me.
I want to add the select options to my table when I use server side processing. Since I have only used version 1.10 I always get confused when I see this old API.
I don't understand how I can use this function in 1.10:
"sAjaxSource": "../examples_support/server_processing.php",
"fnServerData": function ( sSource, aoData, fnCallback ) {
/* ... additional variables ... */
$.getJSON( sSource, aoData, function (json) {
/* Create the select elements on the first run */
if ( json.sEcho == 1 )
{
$("tfoot th").each( function (i) {
/* Insert the select menu */
this.innerHTML = fnCreateSelect(json.select[i]);
/* Add the event listener for the newly created element */
$('select', this).change( function () {
oTable.fnFilter( $(this).val(), i );
} );
} );
}
/* DataTables callback */
fnCallback(json)
} );
}
The converting guide just lists fnServerData as ajax.
This is my initialization code:
$('#dataTables-outputTest').DataTable({
"processing": true,
"serverSide": true,
"ajax": "/TestData/data-source",
"columns": [
{ "data": "thing1",
"searchable": true},
{ "data": "thing2",
"searchable": true},
...
{ "data": "link",
"searchable": false,
"orderable": false},
],
"initComplete": function () {
this.api().columns().every( function () {
var column = this;
var select = $('<select><option value=""></option></select>')
.appendTo( $(column.footer()).empty() )
.on( 'change', function () {
var val = $.fn.dataTable.util.escapeRegex(
$(this).val()
);
column
.search( val ? '^'+val+'$' : '', true, false )
.draw();
} );
column.data().unique().sort().each( function ( d, j ) {
select.append( '<option value="'+d+'">'+d+'</option>' )
} );
} );
}
});
How do I use that function and how do I have to rewrite it to work for me?
It seems I didn't understand what fnServerData did. It can be used to send additional data to the server from the client. So it's the oposite of what I'm trying to do.
I managed to do this by using this code:
"processing": true,
"serverSide": true,
ajax: {
url: '/MyApp/data-source',
data: function ( data ) {
data.someKey = "someValue";
},
},
Then when I print my request on the server I find a new object in the dictionary:
'someKey': ['someValue']

ExtJS store add calls both create and destroy

this.store = Ext.create('Ext.data.Store', {
fields: [
'id',
'name',
'Address',
'status',
],
autoLoad: auto,
autoSync: auto,
remoteSort: true,
proxy: {
type: 'ajax',
api: {
create: '../../create.php',
read: '../../read.php',
destroy: '../../destroy.php',
update: '../../update.php'
},
reader: {
type: 'json',
root: '__data',
totalProperty: 'grandTotal'
},
writer: {
type: 'json',
root: '__data'
},
listeners: {
exception: function( t, response, op ) {
var _da = Ext.decode( response.responseText );
if( _da ) {
if( _da.message == "ExistingName" ) {
_da.message = Locale.gettext('name already exists');
} else {
frm = _self.subnetEditor.down('form');
name_field = frm.down('textfield[name=name]');
}
name_field.markInvalid(Locale.gettext(_da.message));
}
showMsg( _da.success, _da.message );
if( op.action == 'create' || op.action == 'update' ) {
_self.store.rejectChanges();
_self.store.load();
}
}
}
}
}
});
This is the store that calls four php files to do the CRUD, and some listener to process the duplicate name.
removeSelected: function() {
var _self = this;
Ext.Msg.show( {
title: Locale.gettext( 'Remove selected?' ),
msg: Locale.gettext( 'Are you sure you want to remove ALL SELECTED items?' ),
icon: Ext.Msg.WARNING,
buttons: Ext.Msg.OKCANCEL,
buttonAlign: 'right',
fn: function( button ) {
if( button == 'ok' ) {
var grid = _self.down( 'grid' );
if( grid ) {
var selection = grid.getSelectionModel().getSelection();
if( selection.length ) {
_self.store.remove( selection );
if( _self.useGridRowEditing ) {
_self.store.sync();
}
}
}
}
}
} );
}
Here is the remove function will remove the selected items, and I have store.add(item) to add records. But the problem is if I run the remove function, and then store.add to add any items, the store.add will fire create and destroy together. The second destroy will post exact data as the first time when I run the remove function.
I suppose that the store.add will only call the create api in the proxy, but why destroy has been called?
I see the remove throws an exception. If an exception has been thrown, is that mean the remove action is still pending? So it batch the add and remove together?
This is not caused by the ExtJS, it causes the server respond "[null]". The array of null is considered as an exception, I guess the exception causes the request becomes pending.

How to get current rendered element in datatables

I need to change rendering of cell based on the element position, without changing data,
In docs I found mData and mRender options, I tried with fnRender (which is depricated), but it gives me position, but changes data in table while rendering, so I wonder if here is any way to get the cell position when it renders, or I should to rerender table myself after each change ?
the following code illustrate my needs:
$(document).ready( function() {
var oTable = $('#example').dataTable( {
"aoColumnDefs": [ {
"aTargets": ['_all'],
"mRender": function ( data, type, full )
{
if (type!='display') return data;
//HOW to get current cell indexes or DOM element here ?
console.log(this,data,type,full);
return data+' ttt';
}
} ]
} );
$('#example > tbody > tr').on('click','td',function() {
console.log(this);
var pos=oTable.fnGetPosition(this);
var value=oTable.fnGetData(this);
oTable.fnUpdate(value.split("").reverse().join(""),pos[0],pos[2]);
});
var button=$('<button>').text('clickme');
button.click(function() { console.log(oTable.fnGetData());});
$('body').append(button);
});
http://live.datatables.net/enirid/3/edit
the sample using fnRender:
$(document).ready( function() {
var oTable = $('#example').dataTable( {
"aoColumnDefs": [ {
"aTargets": ['_all'],
"fnRender": function ( obj, data )
{
console.log(obj,data);
var col=obj.iDataColumn;
//HOW to change display here without changing data of table
return data+' '+col;
}
} ]
} );
$('#example > tbody > tr').on('click','td',function() {
console.log(this);
var pos=oTable.fnGetPosition(this);
var value=oTable.fnGetData(this);
oTable.fnUpdate(value.split("").reverse().join(""),pos[0],pos[2]);
});
var button=$('<button>').text('clickme');
button.click(function() { console.log(oTable.fnGetData());});
$('body').append(button);
});
http://live.datatables.net/opubaj/edit
Try fnCreatedCell.
Attached the example code snippet provided from datatables.net:
$(document).ready( function() {
$('#example').dataTable( {
"aoColumnDefs": [ {
"aTargets": [3],
"fnCreatedCell": function (nTd, sData, oData, iRow, iCol) {
if ( sData == "1.7" ) {
$(nTd).css('color', 'blue')
}
}
} ]
});
} );

DataTables add column dynamically to table

I'm using DataTables (datatables.net) to display data from an Ajax source and having trouble customizing it. One thing I would like to do is add a column so I can have for example an 'edit' button for each row.
The closest thing to that in the examples is here but I can't get that to work with an ajax source.
Currently, I'm using the following code to display my table:
fnServerObjectToArray = function ( aElements ){
return function ( sSource, aoData, fnCallback ) {
$.ajax( {
"dataType": 'json',
"type": "POST",
"url": sSource,
"data": aoData,
"success": function (json) {
var a = [];
for ( var i=0, iLen=json.aaData.length ; i<iLen ; i++ ) {
var inner = [];
for ( var j=0, jLen=aElements.length ; j<jLen ; j++ ) {
inner.push( json.aaData[i][aElements[j]] );
}
a.push( inner );
}
json.aaData = a;
fnCallback(json);
}
} );
}
}
$(document).ready(function() {
$('#example').dataTable( {
"bProcessing": true,
"sAjaxSource": 'get_data.php',
"fnServerData": fnServerObjectToArray( [ 'username', 'email' ] )
} );
});
Why don't you use fnRenderFunction in the aoColumns? As an example:
aoColumns: [ { "bVisible": false} , null, null, null, null,
{ "sName": "ID",
"bSearchable": false,
"bSortable": false,
"fnRender": function (oObj) {
return "<a href='EditData.php?id=" + oObj.aData[0] + "'>Edit</a>";
}
}
]
You can use it to format the value from the server side.
See similar example on the http://jquery-datatables-editable.googlecode.com/svn/trunk/ajax-inlinebuttons.html (ignore specific settings for the editable plugin)
I've created columns with edit button and links and so on, but usually i do everything server side by custominzg the data i return and then show/hide them with the aoColumns option. I don't really understand what you are tring to achieve: display server side data as a link?
Had the same problem a few months back. This is what I did.
By no means an elegant slution, but this worked.
As you might already know, DataTables do have an overload to accept Javascript Arrays.
So I made by $.ajax call. got my json, parsed it to a javascript array and then while parsing I created an extra element (an anchor tag) with href="edit.php?email=passed_email" Then on the column headers and added a column called Edit. Those values were fed to "aaData" and "aoColumns". And then the table was populated.
And BTW, if you looking for inline editing, check the following link.
DataTables editing example - with jEditableplugin
i have some RND on this problem and get this hope this will help you out.

Categories