DT with Backbone.js
DT with Backbone.js
Allan!
I hope things are going well.
Our company is using Backbone.js for our new applications.
We researched both Knockout and Backbone. I don't know why the architects chose backbone, I can try to find out.
I'm wondering what thoughts you've had towards BB integration?
PJ
I hope things are going well.
Our company is using Backbone.js for our new applications.
We researched both Knockout and Backbone. I don't know why the architects chose backbone, I can try to find out.
I'm wondering what thoughts you've had towards BB integration?
PJ
This discussion has been closed.
Replies
However i am not sure how to proceed when some change in Model data occurs. Upon triggering 'change' event in Model, there will be probably fnUpdate call, however there would need to be some map of models with indexes to be able find correct row. Maybe that map could be kept in the DT ? Backbone's Model has "cid" property which could be used for identification of row and than simple call to fnUpdate giving whole Model would find update displayed data associated with that Model.
I think I need to experiment a bit with this and write up a post about it :-). Any good Backbone.js tutorials you'd recommend?
Btw - I know its a bit different, but there is a lot of overlap, there is a binding available here for Knockout.js and DataTables: https://github.com/CogShift/Knockout.Extensions
Allan
any findings on the DT/Backbone.js integration? I would be interested as well!
/Ben
Allan
First I should say thank you very much for DataTables. It's a pleasure to work with such a full-featured grid component.
I am curious to know what you are working on as far as Backbone.js integration. I have just started using DataTables in my Backbone app, and we're running in to speed issues when there are hundreds of rows. The pagination makes display straightforward, but every time the app tries to sort or filter the list (which I am doing by redrawing the table--is there any other way to?) it pulls the entire array of models from Backbone again. I imagine one solution is to enable DataTables to pull the models in a paginated manner from Backbone. Is this something that your current work might enable?
Thank you,
Drew
Pulling pages data from Backbone would require the implement ion to effectively support the DataTables server-side processing protocol. I'm sure that this is possible, but I'm not sure how practical it would be, since sorting, filtering etc would all need to be implemented in Backbone, where as it is already available on the client-side in DataTables.
Basically yes - I think this is an inherent limitation of using a separate data store such as Backbone - DataTables requires data for sorting etc, and there is a cost associated with pulling that data out of the data store, and this is one of the major drawbacks of using something like this (particularly when there is obviously a significant overlap with what DataTables can do internally).
I'm still experimenting with Backbone - unfortunately I've not had as much time as I would have liked to really get going with it, but I suspect that this will be an issue. I'm surprised that it is showing up with as few as hundreds of rows and I will see what I can do in my own implementation to keep the overhead to a minimum.
Allan
Makes sense. Backbone does itself provide easy ways to define the sorting and filtering of a "collection," but I can see how that would duplicate functionality that DataTables already provides.
Now that I think about it, I wonder if my speed problems are coming from complete reloads of my table. As the user navigates through my single-page app, I use Backbone to change the URL hash and switch from one set of view code to another. For instance, when the user clicks on a line on a map, the table is redrawn and filtered to only show the data entries associated with that line. I set "bDestroy" to true, so does that mean that not only is the table being redrawn in the DOM, but also that DataTables is fetching its entire data array again? Constructing that array in Backbone is, I suspect, a somewhat lengthly process.
Would you recommend that I try to strip away the Backbone route and view code that is probably what triggers a table rebuild on each change and then see how that changes thing? Or do you have another idea?
(I'm glad to post some code, but it's all written in CoffeeScript within the structure of a large single-page app, so it's probably more trouble than it's worth to pull out parts.)
Thanks very much,
Drew
Correct - bDestroy will completely erase the table from Javascript memory and the DOM, requiring a full refresh in order to display the data again.
You mentioned that you are doing a filter - is the data already in the table and it just needs filtering, or do you need to erase the contents of the table an put in something else (for the latter you could use fnClearTable and fnAddData, but that won't be much faster than destroying the whole time).
> Would you recommend that I try to strip away the Backbone route and view code that is probably what triggers a table rebuild on each change and then see how that changes thing?
Possibly... :-). What is your goal with using Backbone and DataTables together? Why did you decide to do that, over just using DataTables directly?
Allan
I have found the examples on here showing how to populate the table using the content of a Backbone collection and the fnServerData callback, but nothing along the lines of working with views. What I think I would like to be able to do is have various views set filter constraints on the collection and then sync the collection with the server using the constraints and have the views update when the sync completes. For instance, the map view might set a constraint that items be within a bounding rectangle and the DataTable could set offset, limit, sort columns/directions, etc.
What I am envisioning is a Backbone View associated with the DataTable with the DataTable configured to generate an "event" (i.e., invoke a method on the view) when any of the settings which affect the table content changes. The View would then be notified by the collection when the new data was available and the table would be redrawn using the new data. I think it might also make sense to wrap each row in a View which would be used to handle events (e.g., a click on a row could toggle a 'selected' flag on the associated model object which might trigger event handlers in different views - perhaps highlight the row in the table and change the icon for the item on the map).
Does this sound like a reasonable approach or would you suggest an alternate route? If so, is there a way to do this in DataTables? It seems like the missing piece is the ability to be notified of a change which would alter the content of the table. I'm thinking I could try having an fnPreDrawCallback that would call _fnAjaxParameters to build the aoData object that is passed to fnServerData and then (deep) compare it with the last value and generate the "event" rather than drawing the table if any aoData values had changed. Alternatively, I could try doing the same thing in fnServerData (which would avoid the need to call _fnAjaxParameters directly) and then just not invoke fnCallback when an event is generated. If it works, I think I prefer the second alternative, but I'm wondering if there's a better way!
Thanks,
Marc
There is an additional project that essentially adds built-in Backbone.js + SlickGrid functionality called SlickBack: http://teleological.github.com/slickback/
I considered going this route, but I believe that DataTable's documentation, community, and support make it the clear winner. Backbone.js and SlickGrid are both awesome tools, but it's hard to beat Allan's commitment to the DataTable's community. It still amazes me that a single human can create, maintain, update, and document all of this code and still have time to respond to pretty much every post on this forum... and then offer it all for free.
Here are just some overlapping functionality between DataTables and Backbone.js (+ Underscore.js): DOM rendering (views), UI event handling, multi-column sorting and filtering, concurrency between data and DOM (change events), text formatting, etc.
However, as jcready rightly says there is a significant degree of overlap between the functionality provided and I suspect most solutions binding the two together will be suboptimal, since there were never designed to work together in this way. Is it mainly binding and live updating that you are looking to get from backbone for DataTables?
Allan
It looks like I have something basically working now (tested the following: populate table from server, sort columns, pagination, and info output). What I am doing is overriding fnServerData to check the aoData against previous values (ignoring sEcho) and triggering an event on the view if the params have changed. A callback is passed with the event which is used to call the fnCallback passed to fnServerData. The event handler sets the search params on the collection and then fetches the collection, with the callback passed as the success handler so fnCallback is ultimately invoked. fnCreatedRow is overridden to create a view for each row, with DOM event handling for the row defined in the row view.
Here is some sample code:
[code]
DtView = Backbone.View.extend({
constraint: undefined,
rows: [],
filterHandler: function(filter, callback) {
this.constraint = filter;
this.collection.setFilter(filter);
this.collection.fetch({success: callback});
},
initialize: function(){
var view = this;
// invoke filter handler when param:change event is triggered
this.bind("param:change", this.filterHandler, this);
// change table element into a DataTable
this.dataTable = $(this.el).dataTable({
"bProcessing": true,
"bServerSide": true,
"sPaginationType": "full_numbers",
"aoColumns": [
{"mDataProp": "col1"},
{"mDataProp": "col2"},
{"mDataProp": "col3"},
{"mDataProp": "col4"},
{"mDataProp": "col5"},
{"mDataProp": "col6"}
],
"fnCreatedRow": function(nRow, aData, iDataIndex){
// create a view for the row
var rowModel = view.collection.get(aData.id);
view.rows.push(new DtRowView({id: aData.id, el: nRow, model: rowModel}));
},
fnServerData: function(sSource, aoData, fnCallback, settings) {
// function used to populate the DataTable with the current
// content of the collection
var populateTable = function()
{
// clear out old row views
rows = [];
// these 'meta' attributes are set by the collection's parse method
var totalSize = view.collection.meta('totalSize');
var filteredSize = view.collection.meta('filteredSize');
return fnCallback({iTotalRecords: totalSize,
iTotalDisplayRecords: filteredSize,
aaData: view.collection.toJSON()});
};
aoData.shift(); // ignore sEcho
var params = $.param(aoData);
if (params != view.constraint)
{
// trigger param:change event with new parameters. Pass in
// populateTable function so fnCallback can be called after
// the data has been refreshed. Note that we cannot just call
// fnDraw on the collection reset event since closure state
// in the fnCallback passed to this method would be lost.
view.trigger("param:change", params, populateTable);
}
else
{
// no filter change, just populate the table
populateTable();
}
}
});
}
});
[/code]
The collection overrides url to append "?params" to the base URL. The server response is similar to the standard DataTables server response, except the full model is returned in aaData rather than just the column values. The collection's parse method is overridden to set the totalSize and filteredSize attributes on the collection and then populates the models using the aaData:
[code]
parse: function(response)
{
this.meta('totalSize', response.iTotalRecords);
this.meta('filteredSize', response.iTotalDisplayRecords);
return response.aaData;
}
[/code]
New here, but also looking at implementing datatables with backbone. My take is, I don't need nor want to duplicate any functionality in datatables as much as possible. I like the way datatables functions as is, my conundrum is the binding. I would love it if there were a simple plugin that simply accepted a collection as the aadata and allowed for functions to be called on each model for mdata so you can manipulate display and filter responses. If we had that I'm done with the backbone integration. I simply want my MODEL to be central and bound to change, don't care about the view or other functionality. Any input on this would be awesome. Thanks.
I'll be writing a blog post about how to do it post 1.10 release (aiming for February).
Allan
Allan
I love this plugin, but the more I work with it. the more I feel like it's just not suitable for working with Backbone js at all. It simply kills the whole binding concept which is one of the main reasons for choosing to work with such a library as Backbone js......
For example, I'd like to be able to bind the rows to Backbone models in a simple way, so when I change a specific MODEL in the collection, the row UI will be updated accordingly .
I know I can't complain since I got DT for free, but I think that DT should support major JS libraries such as Backbone js, Knockout etc. in a way those frameworks will hold the model / data and this plugin will simply be a view layer which can display / search / sort JS model objects.
I'm not sure if version 1.10 will help to support Backbone js in this manner and in a simple way, but if it does, I hope for it to be released soon.
I feel bad for the lack of support of modern js libraries. I think Allan did a great job with this plugin and I'd hate to see all this wonderful work go unused due to lack of compatibility with other technologies.
I'd love to see this plugin taking a step forward and being more compatible with JS libraries such as Backbone js.
Allan
I may have missed your patch to preserve the binding, but may have figured out a different work-around for DataTables to preserve the binding and clean data/presentation separation of Backbone views while letting DataTables be in charge of sorting, searching of data.
The work-around is a variation on the ViewHolder pattern, which may be familiar to you if you do either desktop or mobile development. Basically, the idea is to have each row in the DataTable also store a reference to the Backbone View it is a representation of in a hidden column. This way, when the row gets passed back to the Backbone superview in the fnRowCallback function, you can easily reference its underlying Backbone view, without having to keep track, or manually sort, parallel sets of data. This way DataTables gets to be in charge of sorting, searching, etc, but your Backbone superview never loses track of the Backbone view, and can easily add, remove rows, etc. Furthermore, since you are appending items to the DOM manually through a callback, the binding of the Backbone View's cells remains in tact, and the Backbone view can track scoped events.
I did the following to two of my Backbone Views. The first is called GridView. The GridView is what actually maintains the DataTable, invokes .dataTable(), etc. The second is called GridRowView, which is a single row in the DataTable.
1) Implement a rowCallback() function in my GridView superview.
2) Implement a renderAsArray() function on the view you want to use as the row. This is where the ViewHolder pattern comes in. RenderAsArray will loop through all the views you want to use as cells and append it to the array. But the view will also tack itself onto the end of it.
3) Use fnAddData() to add this array to the table in the GridView superview.
4) DataTables will call fnRowCallback when it tries to render each row. It will pass the 'nRow' parameter which is the actual table row itself, and an aData parameter, which is the row data. At this point you need to clear each cell in that row and replace it with the contents of the renderAsArray(). But how do you get the view the row corresponds to? Easy! You've already stored it in the row itself. Just get the last child of aData and you have the view the nRow represents! This time when you append the result of renderAsArray() the bindings in the child cells will be preserved.
It might be tempting to be suspicious of the ViewHolder pattern but it is very routinely used in Android programming, for instance, when you have a ListAdapter where the positions of objects in a list are constantly changing due to sorting, searching, etc. I see no reason why the same principle can't apply to DataTables.
I hope this is helpful.
Jonathan