Dynamically created header/footer

Dynamically created header/footer

flarpyflarpy Posts: 47Questions: 0Answers: 0
edited November 2012 in General
Hi Allan

I am generating aoColumnDefs server-side and then rendering as normal. For some columns I would like a select filter header, for some an input filter header and for some no filter. I have been playing with a function in fnInitComplete, going through all the aoColumnDefs and creating a footer for each but I can't see an easy way to decide what type of filter to apply

I can't see anything obvious in the docs, can I achieve this using aoColumnDefs maybe something like

[code]
'mData': columnName,
'sClass': niceClass,
'footerType': 'select|input|null',
[/code]

Replies

  • allanallan Posts: 63,534Questions: 1Answers: 10,475 Site admin
    There is no way to do this built into DataTables I'm afraid - columns cannot be built dynamically from an Ajax source (the workaround is to make the Ajax call yourself and then built the DataTable that way) - however, I'd suggest that your approach with fnInitCallback is probably the one I'd take as well - but just ignoring anything internal to DataTables.

    In fnInitCallback you get the JSON from the server (second parameter) so you could loop over your column definitions in that function and add the required markup for the filters there, based on your extra parameters in the JSON object (such as footerType).

    Allan
  • flarpyflarpy Posts: 47Questions: 0Answers: 0
    Hi

    I've got this working using the method discussed; I've added a row in the table header containing the filters for each column. However, when I call oTable.fnFilter it also applies the filter to the thead and so the row with my filters disappears! Any thoughts on how to get round this please?
  • RoyLingRoyLing Posts: 26Questions: 0Answers: 0
    edited November 2012
    Hi flarpy,

    Would you mind sharing more details on how you are generating aoColumnDefs in server-side to create thead dynamically?
    [quote]flarpy said: I am generating aoColumnDefs server-side and then rendering as normal. [/quote]
  • flarpyflarpy Posts: 47Questions: 0Answers: 0
    This is the one of the functions in fnInitComplete - this is work in progress

    [code]
    $('#programtable').find('thead').prepend('');
    for (var e in settings.aoColumns) {
    if (settings.aoColumns[e].bVisible == false) {
    continue;
    }
    if (settings.aoColumns[e].filter != false) {
    var myCells = $('.'+settings.aoColumns[e].mData);
    var innerHTML = fnCreateSelect( oTable.fnGetColumnData(settings.aoColumns[e].mData) );
    console.log(innerHTML);
    var property = settings.aoColumns[e].mData;
    $('#programtable').find('thead tr:first').append(''+innerHTML+'');
    $('select').change( function () {
    oTable.fnFilter( $(this).val(), findColumnNumber(property) );
    } );
    } else {
    $('#programtable').find('thead tr:first').append('test');
    }

    }
    [/code]
  • allanallan Posts: 63,534Questions: 1Answers: 10,475 Site admin
    > However, when I call oTable.fnFilter it also applies the filter to the thead and so the row with my filters disappears!

    Not sure I quite understand I'm afraid. The fnFilter method will apply the filter, but the only place outside the tbody it will effect is the global filtering input, and it will only effect that if you are doing a global filter, rather than a column based filter. fnFilter shouldn't be modifying anything in the thead.

    Allan
  • flarpyflarpy Posts: 47Questions: 0Answers: 0
    @RoyLing - happy to share, will post some php code this morning, here is the concept:

    1. Array of objects that represent columns
    2. Encode object array into JSON
    3. Method to parse JSON to conform to aoColumnDef format
  • flarpyflarpy Posts: 47Questions: 0Answers: 0
    @RoyLing

    [code]
    public static function columns(array $filter = array())
    {
    $columns = array(
    'userAction' => new programColumn(array(
    'name' => 'userAction',
    'label' => '',
    'format' => 'string',
    'bSortable' => false,
    'fieldName' => '',
    'removable' => false,
    'jsRenderFunction' => 'renderAction(data, type, row);',
    'headerLabel' => _('Action'),
    )),
    'name' => new programColumn(array(
    'name' => 'name',
    'label' => _('Program'),
    'format' => 'string',
    'sortDir' => 'asc',
    'fieldName' => 'name',
    'jsRenderFunction' => 'renderNames(data, type, row);',
    'removable' => false,
    )),
    'events' => new programColumn(array(
    'name' => 'events',
    'label' => _('Commissions'),
    'format' => 'events',
    'sortDir' => 'asc',
    'bSortable' => false,
    'sWidth' => '150px',
    'jsRenderFunction' => 'renderEvents(data, type, row);',
    )),
    [/code]

    [code]
    public function datatablesColumnDefinitions(array $columns, array $columnsSelected)
    {
    $defs = array();
    $colNum = 0;

    foreach ($columns as $column) {
    $def = array(
    'mData' => $column->name,
    'sTitle' => $column->label,
    'sClass' => $column->name,
    'bSortable' => isset($column->bSortable) === true ? $column->bSortable : true,
    'sWidth' => isset($column->sWidth) === true ? $column->sWidth : '',
    'aTargets' => array($colNum),
    'filter' => isset($column->filter) === true ? $column->filter : false,
    'bVisible' => in_array($column->name, $columnsSelected) === true ? true: false,
    );
    if ($column->jsRenderFunction !== null) {
    $def['mRender'] = 'function (data, type, row) {return ' . $column->jsRenderFunction . '}';
    }

    $defs[] = (object) $def;
    $colNum++;
    }

    return self::_columnDefsOutput($defs);
    }
    [/code]


    [code]
    protected function _columnDefsOutput($defs)
    {
    $outputString = "[\n";
    $ii = 0;
    foreach ($defs as $key => $object) {
    (isset($object->mRender) === true) ? $myRender = $object->mRender : $myRender = '';
    unset($object->mRender);
    $thisDef = json_encode($object);
    if ($ii > 0) {
    $outputString .= ",\n";
    }

    if ($myRender !== '') {
    // we need to render the function definition unquoted, so splice into in here.
    $outputString .= str_replace('}', ',"mRender":' . $myRender . '}', $thisDef);
    } else {
    $outputString .= $thisDef;
    }

    $ii++;
    }

    $outputString .= ']';

    return $outputString;
    }
    [/code]
  • flarpyflarpy Posts: 47Questions: 0Answers: 0
    edited November 2012
    @Allan

    Hi Allan

    I have tested this by adding a row into thead (using td not th as cells but either produces the same behaviour).

    I then run oTable.fnFilter( 'Automatic', 5);

    It correctly filters content but also removes the row with the filters in.

    Here is some code to demonstrate

    [code]


    test
    test
    test
    AutomaticManual
    test
    test
    test
    test
    test
    test
    test
    test
    test
    test
    test
    test

    .....



    Run oTable.fnFilter( 'Automatic', 5);

    .....
    [/code]
  • allanallan Posts: 63,534Questions: 1Answers: 10,475 Site admin
    Afraid I'm still not seeing how the filtering would effect the header display. Here is an example of how it should work: http://live.datatables.net/izucaq/edit#javascript,html . Can you link me to a test page which shows the issue please?

    Allan
  • flarpyflarpy Posts: 47Questions: 0Answers: 0
    I've found the problem. The table is drawn and then after it has drawn I have added an additional row in the header. On any type of event that causes a redraw, the draw omits the additional header row as it wasn't part of the original table definition.

    I have tried putting the row back with fnDrawCallback but then the filters only contain the item I have just filtered the table by.
  • allanallan Posts: 63,534Questions: 1Answers: 10,475 Site admin
    It still appears to work for me if I dynamically add the row after the table has been initialised: http://live.datatables.net/izucaq/2/edit .

    Allan
  • RoyLingRoyLing Posts: 26Questions: 0Answers: 0
    @flarpy
    Thanks very much for the code snippet.
  • flarpyflarpy Posts: 47Questions: 0Answers: 0
    Hi Allan

    I've setup an environment where you can look at this as I'm stumped:) I'll pm you the login details
  • allanallan Posts: 63,534Questions: 1Answers: 10,475 Site admin
    Thanks for the PM - reposting my message from there, incase any one else is interested in the root cause:

    You've got scrolling enabled - that will do it... The way that DataTables works with scrolling enabled is that it breaks the table into three sections (or two if no footer) - effectively separating the original HTML `table` element into three (or two) different tables.

    Thus a jQuery call such as: $('#example thead') doesn't actually refer to the table's 'visible' header, but rather one inside the visual body of the table which is used to ensure column alignment. DataTables, on each draw, obliterates that inner header, in order to keep visual correctness.

    So there are a couple of options for how to address this:

    1. Get a reference to the thead before initialising the scrolling DataTable and then use that (DataTables moves the original thead into the new header table). So you could do:

    [code]
    var thead = $('#example thead');
    var table = $('#example').dataTable();
    var thead.append( '...' ); // with all the filtering magic of course
    [/code]

    2. You can use the internal cached parameter DataTables uses for the thead element:

    [code]
    var thead = table.fnSettings().nTHead;
    [/code]

    3. Use a selector that reflects the modified DOM structure - such as:

    [code]
    var thead = $('#example_wrapper div.dataTables_scrollHead thead');
    [/code]

    Regards,
    Allan
This discussion has been closed.