How to call a function once from within render?
How to call a function once from within render?
I have a datatable that works fine as follows;
let myTable = jQuery('#ski_index').DataTable({
stateSave: true,
stateDuration: 60 * 60 * 48,
responsive: true,
info: true,
destroy: true,
ajax: function(data, callback) {
callback({ data: aDemoItems })
},
dataType: 'json',
"columns": [
//0
{
data: null,
"defaultContent": "<i>Not set</i>",
render: function (data, type, row){
let fs_data = get_user(data.properties["id"]);
}
},
]
});
The problem arrises with calling get_user(). In fact, the same function is called several times per row. I read this;
1) https://datatables.net/forums/discussion/35550/columns-render-getting-called-multiple-times
2) https://datatables.net/forums/discussion/32996
3) https://datatables.net/manual/data/orthogonal-data
... which explains the reason for it but I am not quite sure how to ensure the function get_user() is only called once.
It seems there each row is called 4 times (?), namely, display, sort, filter and type. If I were to just call the function one, I thought something like below may work
if(type === 'display'){
result = get_user(data.properties["id"]); // get_user() is actually a api call so may be slow. ttl table only 265 rows though.
}
... but that doesnt work and therefore I am not understanding this.
This question has an accepted answers - jump to answer
Answers
I would look at using
columns.createdCell
. It will be called once, the first time the cell is created.Or you could continue to use
columns.render
and use a flag to decide whether to call get_user() or not. You can add a data point to the row data, it doesn't need to be defined incolumns
, and set the flag after calling get_user(). For example:Its unclear from your code snippets what you are trying to do after calling get_user() so not sure if either of these suggestions will work.
Also if get_user() uses ajax to fetch data then its not recommended to use in something like
columns.render
orcolumns.createdCell
.Kevin
Many thanks for the suggestions. I like the idea about setting a flag but not sure if I understand.
The below...
results in Uncaught ReferenceError: data is not defined
The first thing I notice is that you aren't returning anything from the rendering function, so that will result is issues.
However, assuming that
get_user
is an async API call then you absolutely do not want to use it in the rendering function. The renderer is always synchronous with no way to do a callback or Promise on completion of an async action. It is also called many times.Don't do it
You are Ajax getting the data (well, that is how it is configured...), so what I would strongly suggest is that you build the full data structure that you need on the server-side. i.e. resolve the
get_user
information there. If it is a database call it will be so much faster than using Ajax, even if it does mean a little bit of extra code.If you must resolve it on the client-side for whatever reason, then do it in the
ajax
function where you are currently doingcallback({ data: aDemoItems })
. Before making the callback, resolve the extra data you need.It isn't clear where
aDemoItems
is coming from - maybe its own Ajax call or something else.Resolving at source will be the most performant way for your end users.
Allan
Thank you for your help. I thought I would simplify my question by making a use case similar to my problem but maybe I need to tell you my entire problem...
I am not a programmer but a hobbiest so excuse my poor coding ;-). I combined a mapbox map and datatable here: https://www.freshsnow.jp/freshsnow_ski_index/#4.2/39/137.84/0/60. As the view zooms in and out, the datatable refreshes to show ski mountains currently in view.
In the datatable and on the far right side, you will see a "VIEW" link. View is a link to that specific mountain (a single mountain). The URL is generated with this:
aaDemoItems
is an ajax call to mapbox api which returns a json object of ski resorts. It directs to a webpage that only shows one page but theaa_*
data is important because it puts the mountain in the map view at the desiredlat/lon, zoom, bearing, etc.
The issue is that all theaa_*
is generated by QGIS and not always optimal. Sometimes, for example, theaa_zoom
would be better zoomed in or out, or thebearing
is off. The calculations made in QGIS works about 70% of the time. For the other 30%, I have a local database (using wordpress and custom post types). In WP, I have the identical table as on mapbox but if populated with local data, the program is to use that data. So I called itget_user()
to make it easy but actually I am usingget_fs_ski_resort()
.get_fs_ski_resort()
is an api call that returns afs_ski_resort
object. The api call determines if it uses the remote mapbox data or the local database data (eg override). All that logic is inget_fs_ski_resort()
Sorry for the long post. I am just a hobbiest programmer so the code is rather poor but I would appreciate and further pointers.
How would it be best to get the data from
get_fs_ski_resort()
in datatables.I got what you mean about async and sync calls but not sure how I would go about building the data object server side as it will get rather complex. I have events such as moveend and moveidle that requires mapbox and returns the json. Would be so much easier if I could just overide the data somehow, when needed.
One option is to use
createdRow
. It will be called only once when the row is added to the document. This will run once for each row during initialization. You could enabledeferRender
and thecreatedRow
will only operate on the rows displayed on the page. See this simple example:https://live.datatables.net/qehepivi/2/edit
The ajax call simulates your
get_fs_ski_resort()
call. The Office html update is there just to show how the async update works.With
deferRender
you will see that only 10 rows executedcreatedRow
. Go to the next page then the next 10. RemovedeferRender
and clickRun with JS
and you will see all 57 rows executecreatedRow
.Another option, as Allan mentioned, might be to initially load all the
get_fs_ski_resort()
data into an object data structure with the key being thedata.properties["id"]
. It may be that one ajax request for all the data might be more efficient than one request for each row. I've done this before and it works well. Just depends on the size of the data.Kevin
Thank you very much @allan and @kthorngren. I tried to load the data once the map is loaded into an object. It takes a few seconds which is probably too slow. I tried the createdRow and that's awesome. I really like the deferRender functionality which makes it super fast.
In the eleventh column, of each row, I have a VIEW link. Sorry but how do I replace the html in that cell/column?
Sorry, I figured it out. Many thanks. Datatables is very powerful. Thank you for all the detailed help!
Thanks for the update - good to hear you got it going.
Allan