Caching mRender
Caching mRender
Hi,
I am trying to figure out how to cache the values generated in mRender.
In short, the data I'm sending back from the server contains a large list of boolean values. In order to reduce the data being transmitted and lessen the load on the server I am encoding these booleans as number (0 or 1) and decoding these at the client side to the actual image.
I've successfully gotten this to work and loading is much faster, however filtering is slower than it was before. I also want all operations (filtering, sorting, display etc) to be done on the decoded data, so I thought mData was the right place to do this, but I wasn't able to because the mData function doesn't get the column index, so I was never able to figure out which column was being rendered.
So my question, currently when filtering there is a slight 1-2sec delay as it I assumes rerenders the rows to search. Is there anyway I can optimize this and remove this lag? I don't mind a one time lag for the first rendering.
my mRender is:
[code]
// Render image columns
{
"mRender": function ( data, type, value ) {
if( type === 'display' || type === 'filter' ){
return lookupImage(data);
}
return data;
},
"aTargets": [ 3, 5, 7, 10, ... ]
}
[/code]
I have also tried using mData as written http://datatables.net/blog/Orthogonal_data here, by making one mData function for every column and hardcoding the column names. This ended up with the same performance as using mRender. Am I doing something wrong?
Also since I have a lot of booleans per row, I was thinking of encoding them all in a single int per row. However again it's difficult because the column index isn't given to mRender and mData. Anyone know a way around this?
I am trying to figure out how to cache the values generated in mRender.
In short, the data I'm sending back from the server contains a large list of boolean values. In order to reduce the data being transmitted and lessen the load on the server I am encoding these booleans as number (0 or 1) and decoding these at the client side to the actual image.
I've successfully gotten this to work and loading is much faster, however filtering is slower than it was before. I also want all operations (filtering, sorting, display etc) to be done on the decoded data, so I thought mData was the right place to do this, but I wasn't able to because the mData function doesn't get the column index, so I was never able to figure out which column was being rendered.
So my question, currently when filtering there is a slight 1-2sec delay as it I assumes rerenders the rows to search. Is there anyway I can optimize this and remove this lag? I don't mind a one time lag for the first rendering.
my mRender is:
[code]
// Render image columns
{
"mRender": function ( data, type, value ) {
if( type === 'display' || type === 'filter' ){
return lookupImage(data);
}
return data;
},
"aTargets": [ 3, 5, 7, 10, ... ]
}
[/code]
I have also tried using mData as written http://datatables.net/blog/Orthogonal_data here, by making one mData function for every column and hardcoding the column names. This ended up with the same performance as using mRender. Am I doing something wrong?
Also since I have a lot of booleans per row, I was thinking of encoding them all in a single int per row. However again it's difficult because the column index isn't given to mRender and mData. Anyone know a way around this?
This discussion has been closed.
Replies
I think so. I'm not sure you're completely clear on how mData can benefit you in this situation.
In the link you provided, look at the "Currency example" section. You want to utilize the "set" type to cache the markup string you're generating from lookupImage. Something like this...
[code]mRender: function(data, type, value) {
if(type === 'set') {
// this will only be called one time per row!
data.cached_image_string = lookupImage(data);
}
if(type === 'filter' || type === 'display') {
// instead of re-calculating the markup for this image,
// use the string we calculated earlier each successive
// call to this row (i.e. display, filter)
return data.cached_image_string;
}
}[/code]
Now, this may look confusing -- data is an array, but I'm assigning it an arbitrary property. Remember though, arrays in JavaScript are just a special type of object (but still an object nonetheless), so we can give it properties like this, as if it were a hash/object [i.e. {} ].
Also, you're probably going to have to go back to what you did earlier -- hard-coding column names for each call to mRender. Then you can expand it to look something like this:
[code]function findStatusImage(value) {
// show a specific image based on the bit value
var image_name = "/status_image_" + value + "_.png";
return "";
}
function findOtherImage(value) {
var image_name = "/other_image_" + value + "_.png";
return "";
}
' // ignore this ...
// we're storing two values in here: "Raw" and "Markup";
// "Raw" is the value that the server gave us.
// "Markup" is the resultant string from lookupImage.
function parse_column(data, type, value, columnName) {
if(type === 'set') {
// preserve the original value -- the boolean/int
// your server is returning; this allows us to preserve
// this if we need it later, and gives us a fallback value
// to use as well...
data[columnName + "-Raw"] = value;
// now handle each specific column, building the markup
// and the associated image
switch(columnName) {
case "StatusImageColumn":
data["StatusImageColumn-Markup"] = findStatusImage(value);
break;
case "SomeOtherImageColumn":
data["OtherImageColumn-Markup"] = findOtherImage(value);
break;
}
}
if(type === 'display') {
// check to see if we have a cached image string available;
// this is looking at the values we generated in the switch statement
// from the "type" conditional above
// if we don't, fall back to using the raw boolean value
return data[columnName + "-Markup"] || data[columnName + "-Raw"];
}
// etc
}
$("#myTable.")dataTables({
aoColumns: [
{
sName: "StatusImageColumn",
bSortable: false,
mRender: function(data, type, value) {
return parse_column(data, type, value, "StatusImageColumn";
},
{
sName: "SomeOtherImageColumn",
bSortable: false,
mRender: function(data, type, value) {
return parse_column(data, type, value, "SomeOtherImageColumn";
},
]
})[/code]
Also, do all of the images you're generating have an explicit height and width set? This would help get rid of flickering that may occur (otherwise the browser is pulling the image from its cache, calculating its dimensions, then re-sizing the image element to the size of the image).
Hope this helps... let me know if you need anything cleared up.
Allan
My understanding from the documentation is that mData is set before mRender since mRender is set on the data received in mData. Therefore I figured mData would be the best place since I always want the decoded values.
In your examples you use 'set' in mRender, however according to the documentation mRender doesn't have a 'set' type. it says
"{string} The type call data requested - this will be 'filter', 'display', 'type' or 'sort'."
where mData says:
"{string} The type call data requested - this will be 'set' when
setting data or 'filter', 'display', 'type', 'sort' or undefined when
gathering data. Note that when undefined is given for the type
DataTables expects to get the raw data for the object back"
Which is why I tried it with mData,I had a similar implementation to what you've posted for mData, e.g. create a function that takes the column index and in set generates the data and on filter and display returns it. I did not however see any noticeable difference.. Maybe I did something wrong I'll try again, I do like the approach of extending the data array itself. Hadn't thought of that :).
The images don't need to be resized as they're just icons so we show them unscaled.
If mRender should have a 'set' type then I'll give that a try.
Sorry, you're correct -- I had the two mixed up. mRender basically exposes a subset of the features in mData. They share some common ground though. I think you want to switch my example to mData and use that. That's the strategy I would recommend using, because "set" will be an optimization.
[quote]The images don't need to be resized as they're just icons so we show them unscaled.[/quote]
Even if they're unscaled, you should still specify the size of the image. The browser doesn't know how large the image is until it downloads the image and can read the dimensions of it. If you give it explicit dimensions in advance, it can render an empty placeholder element while it's loading the image. This will reduce visual flickering, and may end up making the process slightly more smooth.