Adding a property obtained asynchronously from the database, to data object

Adding a property obtained asynchronously from the database, to data object

miwarrenmiwarren Posts: 5Questions: 2Answers: 0

I have Objects that I pull from the database (in the code base, we perform this request ourselves, and then process each Object into a client-side Employee object, and then pass that to Datatables). Employee class, for ease of interacting with the backend, has the following properties:

  • Id
  • FirstName
  • LastName
  • IsActive
  • PictureId
  • Address

Our setup for Datatables is as follows:

paging : true,
stateSave : true,
deferRender : true,
columns: [
        { 
          data : "image"
        },
        { 
          data : "Id" 
        },
        { 
          data : "FirstName" 
        },
        { 
          data : "LastName" 
        },
        { 
          data : "IsActive" 
        },
        { 
          data : "Action" 
        }
      ]

Since we want our table to look something like the following:

and since pictures are requested asynchronously, we have additional Datatables setup logic:

columnDefs : [
        {
          targets: 0,
          render: function(data, type, row) { 
            return `<img class="content-image person-image" src="data.Picture" onerror="this.src='/static/images/no-image.png';" alt="Employee Image" height="45" width="45">`
          }
        },
        {
          targets: 4,
          render: function(data, type, row) { 
            return `<label class="tgl">
                <input onclick="ToggleEmployeeActive(this,event)" class="form-isactive" type="checkbox" ${(data) ? 'checked' : ''} />
                <span class="tgl_body">
                    <span class="tgl_switch"></span>
                    <span class="tgl_track">
                        <span class="tgl_bgd"></span>
                        <span class="tgl_bgd tgl_bgd-negative"></span>
                    </span>
                </span>
            </label>`
          }
        },
        // the action row
        {
          targets : 5,
          render : function(data, type, row) { 
            return `
<button class="btn btn-danger" onclick="DeleteEmployee(this)">
                <i class="fa fa-trash-o" aria-hidden="true"></i>
            </button>
            <button class="btn btn-primary" onclick="PopModel(this)">
                <i class="fa fa-pencil" aria-hidden="true"></i>
            </button>
            <button class="btn btn-success" onclick="PopStores(this)">
                <i class="fa fa-map-marker" aria-hidden="true"></i>
            </button>`
          }
        }
      ],
      /**
       * Invoked when a row is being created
       * @param {DomRow} row the row that datatables comes up with
       * @param {Object | Array} data the data we pass to datatables for the current row
       */
      createdRow: function(row, data) {
          data.GetPicture(function(picture) { 
            console.log("pic obtained from database")
            data.image = picture
          })
      }

Issue is that, on here, the data.image I create in createdRow is not accessible in columnDefs.render !

Here is live demo of the problem I'm facing (as of now, it is connected to fake API via Postman, which is throwing HTTP 429 (too many requests) error, because of how many times the page refreshed in my attempts to solve this issue.

How can I get the data from the image requests in the table?

This question has an accepted answers - jump to answer

Answers

  • kthorngrenkthorngren Posts: 21,309Questions: 26Answers: 4,947

    I'm not sure exactly how your get picture method works but I don't think createdRow or rowCallback are meant to allow for updating the data in the table. They are more for setting attributes, etc on the row.

    I suspect that once the pictures are loaded there is a way to correlate the picture to a data element in each row. If so then one option is to use rows().every() to loop through all the rows once the pictures are retrieved and set the data.image column to that picture.

    Kevin

  • miwarrenmiwarren Posts: 5Questions: 2Answers: 0
    edited January 2019

    I put in that rows().every() method, and noticed that it runs for all of the rows in the table's rowspace, not just the ones currently in the view. This is disaster, because it means it's going to run for all 16530 rows that are in the table in the real version of what I'm working on! That's potentially 16530 data requests!!

    There has got to be a better way: to somehow check for the presence of the property of the data in the view, and if it's not there, make request to get its value and set it (and maybe even update the view accordingly).

  • kthorngrenkthorngren Posts: 21,309Questions: 26Answers: 4,947
    Answer ✓

    ll of the rows in the table's rowspace, not just the ones currently in the view

    You can use the selector-modifier of {page:'current'} of the rows() API. Here is an example:
    https://datatables.net/reference/type/selector-modifier#page

    Does this help?

    If not then please describe a bit more detail of the process of fetching the pictures. Your test case has a lot of code. Since it is not executing, due to the error you mentioned, it would difficult to scan and understand what is going on.

    Based on your last comment it sounds like the pictures are fetched individually for each row. Not a bulk load like I assumed earlier :smile:

    If this is correct then my recommendation would be this:

    1. Use either the drawCallback or the draw event and inside use rows().every() with {page:'current'} to loop through each displayed row.
    2. Get the data for the row using row().data().
    3. If the picture is there then skip to the next picture.
    4. If not there then fetch the picture and update the row using row().data().

    HTH,

    Kevin

  • miwarrenmiwarren Posts: 5Questions: 2Answers: 0
    edited January 2019

    This answered the question, but left behind a side effect: the first column's size and content positioning not adjusting. I should probably make that a separate question lol

This discussion has been closed.