1
0
mirror of https://github.com/DataTables/DataTables.git synced 2025-03-15 16:29:16 +01:00

Update: Rewrite of column type detection

- Automatic column type detection was a real weak point of v1.9- - it
  did basically work, but if you then updated a row that didn't match
  the current data type it would always end up as a string. A good
  example of this is the ambiguous date "06-06-13" (is it dd-mm-yy or
  mm-dd-yy?). If it was detected as dd-mm-yy and then you add '05-20-13'
  to the column (or update an exisiting cell), the type would not match
  the exisiting value that thus failover to a string.

- Type detection is now more rigorous, but still optimised (since it
  has the potential to take up a significant amount of time). When a row
  is added or updated, or a cell is updated, the exisiting type is
  removed from the target column(s) and then, before sorting or
  filtering, the _fnColumnTypes function checks to see if any column
  needs to be type detected and do so if needed. This approach allows
  multiple rows to be added (for example) before the draw is performed and
  the type actually needs to be calculated.

- In future I'd like to have a 'data-ready' type event which will tell
  DataTables, and any of its components that something wants to work with
  the data in the table and it should prep the data. The counterpart would be
  a 'data-invalid' flag which would be set on update, add etc so it knows
  when an update is needed.
This commit is contained in:
Allan Jardine 2013-07-30 19:06:25 +01:00
parent d9ce185f35
commit 16dea34d8c
2 changed files with 94 additions and 61 deletions

View File

@ -1 +1 @@
b4fbf198ab4d7ed709fd4c35dd3201f877408361 01c0282a6f2b9b9ff6db0b05bffda9c30ef93d07

View File

@ -281,11 +281,7 @@
oOptions.mData = oOptions.mDataProp; oOptions.mData = oOptions.mDataProp;
} }
if ( oOptions.sType !== undefined ) oCol._sManualType = oOptions.sType;
{
oCol.sType = oOptions.sType;
oCol._bAutoType = false;
}
// `class` is a reserved word in Javascript, so we need to provide // `class` is a reserved word in Javascript, so we need to provide
// the ability to use a valid name for the camel case input // the ability to use a valid name for the camel case input
@ -448,30 +444,58 @@
} }
/** function _fnColumnTypes ( settings )
* Get the sort type based on an input string
* @param {string} sData data we wish to know the type of
* @returns {string} type (defaults to 'string' if no type can be detected)
* @memberof DataTable#oApi
*/
function _fnDetectType( sData )
{ {
var aTypes = DataTable.ext.type.detect; var columns = settings.aoColumns;
var iLen = aTypes.length; var data = settings.aoData;
var types = DataTable.ext.type.detect;
var i, ien, j, jen, k, ken;
var col, cell, detectedType, cache;
for ( var i=0 ; i<iLen ; i++ ) // For each column, spin over the
{ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
var sType = aTypes[i]( sData ); col = columns[i];
if ( sType !== null ) cache = [];
{
return sType; if ( ! col.sType && col._sManualType ) {
col.sType = col._sManualType;
}
else if ( ! col.sType ) {
for ( j=0, jen=types.length ; j<jen ; j++ ) {
for ( k=0, ken=data.length ; k<ken ; k++ ) {
// Use a cache array so we only need to get the type data
// from the formatter once (when using multiple detectors)
if ( cache[k] === undefined ) {
cache[k] = _fnGetCellData( settings, k, i, 'type' );
}
detectedType = types[j]( cache[k] );
// Doesn't match, so break early, since this type can't
// apply to this column
if ( ! detectedType ) {
break;
}
}
// Type is valid for all data points in the column - use this
// type
if ( detectedType ) {
col.sType = detectedType;
break;
}
}
// Fall back - if no type was detected, always use string
if ( ! col.sType ) {
col.sType = 'string';
}
} }
} }
return 'string';
} }
/** /**
* Get the column ordering that DataTables expects * Get the column ordering that DataTables expects
* @param {object} oSettings dataTables settings object * @param {object} oSettings dataTables settings object
@ -585,8 +609,6 @@
*/ */
function _fnAddData ( oSettings, aDataIn, nTr, anTds ) function _fnAddData ( oSettings, aDataIn, nTr, anTds )
{ {
var oCol;
/* Create the object for storing information about this new row */ /* Create the object for storing information about this new row */
var iRow = oSettings.aoData.length; var iRow = oSettings.aoData.length;
var oData = $.extend( true, {}, DataTable.models.oRow, { var oData = $.extend( true, {}, DataTable.models.oRow, {
@ -598,31 +620,11 @@
/* Create the cells */ /* Create the cells */
var nTd, sThisType; var nTd, sThisType;
for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) var columns = oSettings.aoColumns;
for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
{ {
oCol = oSettings.aoColumns[i];
_fnSetCellData( oSettings, iRow, i, _fnGetCellData( oSettings, iRow, i ) ); _fnSetCellData( oSettings, iRow, i, _fnGetCellData( oSettings, iRow, i ) );
columns[i].sType = null;
/* See if we should auto-detect the column type */
if ( oCol._bAutoType && oCol.sType != 'string' )
{
/* Attempt to auto detect the type - same as _fnGatherData() */
var sVarType = _fnGetCellData( oSettings, iRow, i, 'type' );
if ( sVarType !== null && sVarType !== '' )
{
sThisType = _fnDetectType( sVarType );
if ( oCol.sType === null )
{
oCol.sType = sThisType;
}
else if ( oCol.sType != sThisType && oCol.sType != "html" )
{
/* String is always the 'fallback' option */
oCol.sType = 'string';
}
}
}
} }
/* Add to the display array */ /* Add to the display array */
@ -1085,9 +1087,10 @@
* the sort and filter methods can subscribe to it. That will required * the sort and filter methods can subscribe to it. That will required
* initialisation options for sorting, which is why it is not already baked in * initialisation options for sorting, which is why it is not already baked in
*/ */
function _fnInvalidateRow( settings, rowIdx, src ) function _fnInvalidateRow( settings, rowIdx, src, column )
{ {
var row = settings.aoData[ rowIdx ]; var row = settings.aoData[ rowIdx ];
var i, ien;
// Are we reading last data from DOM or the data object? // Are we reading last data from DOM or the data object?
if ( src === 'dom' || (! src && row.src === 'dom') ) { if ( src === 'dom' || (! src && row.src === 'dom') ) {
@ -1098,13 +1101,25 @@
// Reading from data object, update the DOM // Reading from data object, update the DOM
var cells = row.anCells; var cells = row.anCells;
for ( var i=0, ien=cells.length ; i<ien ; i++ ) { for ( i=0, ien=cells.length ; i<ien ; i++ ) {
cells[i].innerHTML = _fnGetCellData( settings, rowIdx, i, 'display' ); cells[i].innerHTML = _fnGetCellData( settings, rowIdx, i, 'display' );
} }
} }
row._aSortData = null; row._aSortData = null;
row._aFilterData = null; row._aFilterData = null;
// Invalidate the type for a specific column (if given) or all columns since
// the data might have changed
var cols = settings.aoColumns;
if ( column !== undefined ) {
cols[ column ].sType = null;
}
else {
for ( i=0, ien=cols.length ; i<ien ; i++ ) {
cols[i].sType = null;
}
}
} }
@ -2263,6 +2278,10 @@
oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive; oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
}; };
// Resolve any column types that are unknown due to addition or invalidation
// @todo As per sort - can this be moved into an event handler?
_fnColumnTypes( oSettings );
/* In server-side processing all filtering is done by the server, so no point hanging around here */ /* In server-side processing all filtering is done by the server, so no point hanging around here */
if ( !oSettings.oFeatures.bServerSide ) if ( !oSettings.oFeatures.bServerSide )
{ {
@ -3901,7 +3920,6 @@
var var
i, ien, iLen, j, jLen, k, kLen, i, ien, iLen, j, jLen, k, kLen,
sDataType, nTh, sDataType, nTh,
aSort = [],
aiOrig = [], aiOrig = [],
oExtSort = DataTable.ext.type.sort, oExtSort = DataTable.ext.type.sort,
aoData = oSettings.aoData, aoData = oSettings.aoData,
@ -3910,9 +3928,13 @@
formatters = 0, formatters = 0,
nestedSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting ), nestedSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting ),
sortCol, sortCol,
displayMaster = oSettings.aiDisplayMaster; displayMaster = oSettings.aiDisplayMaster,
aSort = _fnSortFlatten( oSettings );
aSort = _fnSortFlatten( oSettings ); // Resolve any column types that are unknown due to addition or invalidation
// @todo Can this be moved into a 'data-ready' handler which is called when
// data is going to be used in the table?
_fnColumnTypes( oSettings );
for ( i=0, ien=aSort.length ; i<ien ; i++ ) { for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
sortCol = aSort[i]; sortCol = aSort[i];
@ -5408,7 +5430,6 @@
"_fnGetWidestNode": _fnGetWidestNode, "_fnGetWidestNode": _fnGetWidestNode,
"_fnGetMaxLenString": _fnGetMaxLenString, "_fnGetMaxLenString": _fnGetMaxLenString,
"_fnStringToCss": _fnStringToCss, "_fnStringToCss": _fnStringToCss,
"_fnDetectType": _fnDetectType,
"_fnSettingsFromNode": _fnSettingsFromNode, "_fnSettingsFromNode": _fnSettingsFromNode,
"_fnGetDataMaster": _fnGetDataMaster, "_fnGetDataMaster": _fnGetDataMaster,
"_fnEscapeRegex": _fnEscapeRegex, "_fnEscapeRegex": _fnEscapeRegex,
@ -7982,7 +8003,7 @@
// Set // Set
_fnSetCellData( ctx[0], cell[0].row, cell[0].column, data ); _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
_fnInvalidateRow( ctx[0], cell[0].row, 'data' ); _fnInvalidateRow( ctx[0], cell[0].row, 'data', cell[0].column );
return this; return this;
} ); } );
@ -8613,13 +8634,13 @@
"bVisible": null, "bVisible": null,
/** /**
* Flag to indicate to the type detection method if the automatic type * Store for manual type assignment using the `column.type` option. This
* detection should be used, or if a column type (sType) has been specified * is held in store so we can manipulate the column's `sType` property.
* @type boolean * @type string
* @default true * @default null
* @private * @private
*/ */
"_bAutoType": true, "_sManualType": null,
/** /**
* Flag to indicate if HTML5 data attributes should be used as the data * Flag to indicate if HTML5 data attributes should be used as the data
@ -13534,7 +13555,8 @@
return a.toLowerCase(); return a.toLowerCase();
}, },
// string-asc and -desc are retained only for compatibility with // string-asc and -desc are retained only for compatibility with the old
// sort methods
"string-asc": function ( x, y ) "string-asc": function ( x, y )
{ {
return ((x < y) ? -1 : ((x > y) ? 1 : 0)); return ((x < y) ? -1 : ((x > y) ? 1 : 0));
@ -13606,6 +13628,17 @@
} }
] ); ] );
// date
// numeric (inc. formatted)
// html numbers (inc. formatted)
// html
add sort types (currency, html numbers, formatted numbers, formatted html numbers)
currency is just formatted numbers
// Filter formatting functions. See model.ext.ofnSearch for information about // Filter formatting functions. See model.ext.ofnSearch for information about