How to specify data type of DOM sourced data?

How to specify data type of DOM sourced data?

sliekenssliekens Posts: 97Questions: 17Answers: 2
edited November 2016 in Free community support

If I use the DOM as a data source, the DataTable treats all values as string types.

How can I tell the DataTable to parse a column as number, boolean or something else?

Test case: https://plnkr.co/edit/YO9YGrQgZ6cEtIyZduiP?p=preview

This test case adds three rows via the DOM and a fourth row via the row.add() API. Pay close attention to the data types that I get from the createdRow callback.

tldr if the DOM contains <td>false</td> then how can I initialize a DataTable that internally uses the boolean false instead of the string "false"?

Answers

  • sliekenssliekens Posts: 97Questions: 17Answers: 2

    Apparently it is possible to mutate the data before the first draw.

    dt.createdRow = function(row, data, dataIndex) {
      if (typeof data.id === 'string') {
        data.id = parseInt(data.id);
      }
      
      if (typeof data.done === 'string') {
        data.done = (data.done === 'true');
      }
    }
    

    I'd still like to know if there is a better way. Can it be done through configuration? Or with a (custom) plugin?

  • allanallan Posts: 63,195Questions: 1Answers: 10,412 Site admin

    If you are reading from the DOM, then yes, they are always read as strings, although sorting will correctly detect numbers. There currently is no defined way to cast the type while reading from the DOM. The only way is a workaround as you have done.

    Allan

  • sliekenssliekens Posts: 97Questions: 17Answers: 2
    edited November 2016

    Followup question: would you mind adding a callback to the API for deserializing DOM data?

    columns: [
      // ID
      {
        data: 'id',
        visible: false,
        // deserialize DOM data as number
        deserialize: function(text) {
          return parseInt(text);
        }
      },
      // Task
      {
        data: 'task'
      },
      // Done
      {
        data: 'done',
        // deserialize DOM data as boolean (using a truthy/falsy shortcut)
        deserialize: function(text) {
          return text === 'true';
        }
      }
    ]
    
  • allanallan Posts: 63,195Questions: 1Answers: 10,412 Site admin

    I should qualify my statement above - its actually possible already if you use columns.data as a function - it isn't as trivial as I was thinking above, but it can be done, you just need to do the deserialisation. You need to be a little careful incase it is called multiple times of course, but a type check would handle that.

    Allan

  • sliekenssliekens Posts: 97Questions: 17Answers: 2
    edited November 2016

    I don't think I can use a function. I need to also use the ajax option for async CRUD operations. If I set columns.data to a function that expects initial data to be string then I can't re-use that function for ajax CRUD operations, can I?

    Initial data sourced from the DOM

    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>Task</th>
          <th>Completed</th>
        </tr>
      </thead>
      <tr>
        <td>1</td>
        <td>Initialize DataTables + Editor</td>
        <td>false</td>
      </tr>
    </table>
    
    

    AJAX refresh (eg. after using Editor to change the Completed status)

    {
      data: [
        {
          "id": 1,
          "task": "Initialize DataTables + Editor"
          "completed": true
        }
      ]
    }
    

    I can't imagine what a columns.data function for this scenario would look like.

  • allanallan Posts: 63,195Questions: 1Answers: 10,412 Site admin

    I can't re-use that function for ajax CRUD operations, can I?

    I don't see why not, although I don't fully understand that point and how it relates to CRUD.

    I've just put a little example together here: http://live.datatables.net/nipicuqo/1/edit .

    Allan

  • sliekenssliekens Posts: 97Questions: 17Answers: 2
    edited November 2016

    Let's walk through my problem from the very beginning.

    My setup begins with a pre-rendered HTML table.

    <table id="example">
      <thead>
        <tr>
          <th>Name</th>
          <th>Age</th>
        </tr>
      </thead>
      <tr>
        <td>Tiger Nixon</td>
        <td>61</td>
      </tr>
      <tr>
        <td>Garrett Winters</td>
        <td>63</td>
      </tr>
     </table>
    

    By default, DataTables parses each row as an array of string.

    [
      'Tiger Nixon',
      '61'
    ],
    [
      'Garrett Winters',
      '63'
    ],
    

    You showed me how to add parsing behavior by attaching functions to columns.data

    columnDefs: [
      {
        targets: 0,
        data: function ( row, type, set, meta ) {
          if ( type === 'set' ) {
            row[0] = set;
          }
          return row[0];
        }
      },
      {
        targets: 1,
        data: function ( row, type, set, meta ) {
          if ( type === 'set' ) {
            row[1] = parseInt( set, 10 );
          }
          
          return row[1];
        }
      }
    ]
    

    ...but the resulting row object is still an array (of mixed types).

    [
      'Tiger Nixon',
      61
    ],
    [
      'Garrett Winters',
      63
    ],
    

    The point where I get stuck is when I need to replace the data in the table with a copy from a web server.

    Suppose that I have configured ajax to get data from a custom backend

    $('#example').DataTable().ajax.reload();
    

    The backend returns this data:

    {
      data: [
        {
          name: 'Tiger Nixon',
          age: 61
        },
        {
          name: 'Garrett Winters',
          age: 63
        }
      ]
    }
    

    The response contains an array of objects. Those objects are passed to my function which assumes that row is an array, but here row is an object.

    DataTables warning: table id=example - Requested unknown parameter '0' for row 0, column 0.

    Repro: http://live.datatables.net/faxecere/2/edit (push the Reload button and look at the console output)

    It gets even worse when I try to implement Editor, which has another AJAX option that needs to be configured.

  • allanallan Posts: 63,195Questions: 1Answers: 10,412 Site admin

    ...but the resulting row object is still an array (of mixed types).

    Right, instead of writing to an index value like I had above (I didn't know you were using objects), you'd write to an object parameter:

    row.age = parseInt( set, 10 );
    

    Allan

  • allanallan Posts: 63,195Questions: 1Answers: 10,412 Site admin

    I should say, this is why I don't recommend using columns.data as a function very often. It can be confusing as ****.

    Allan

  • sliekenssliekens Posts: 97Questions: 17Answers: 2

    Thanks, that works. The pieces of the puzzle are starting to fall into place now.

  • sliekenssliekens Posts: 97Questions: 17Answers: 2
    edited December 2016

    -removed-

  • sliekenssliekens Posts: 97Questions: 17Answers: 2

    Allan, is it possible that parsing fields to numbers doesn't work for ID columns? (DT_RowId or custom RowId)

    <!-- Every row starts with a numeric column that is the database ID of the row -->
    <thead>
      <tr>
        <th>ID</th>
      </tr>
    </thead>
    <tr id="1">
      <td>1</td>
    </tr>
    <tr id="2">
      <td>2</td>
    </tr>
    
    // the AJAX backend returns objects with "id" fields for that column
    {
        data: [
          {
              id: 1
          },
          {
              id: 2
          }
        ]
    }
    
    // DataTable is configured to use 'id' instead of 'DT_RowId'
    // columns.data is a function that parses 'id' values to numbers
    var options =
    {
        rowId: 'id',
        columns: [
          {
              data: function (row, type, set, meta) {
                if (type === 'set') {
                  row.id = parseInt(set, 10);
                }
                return row.id;
              }
          }
        ]
    }
    

    Am I doing it wrong or is there something baked into DataTable/Editor that forces the value of rowId to be a string?

  • allanallan Posts: 63,195Questions: 1Answers: 10,412 Site admin

    Yes - because it is read from the DOM it is always a string. There isn't a way to change that at the moment I'm afraid.

    Allan

This discussion has been closed.