DT with Backbone.js

DT with Backbone.js

PJPJ Posts: 7Questions: 0Answers: 0
edited August 2011 in General
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

Replies

  • FredyCFredyC Posts: 4Questions: 0Answers: 0
    Hello. I am interested in this topic too, because i am using Backbone.js extensively. From my short journey through documentation i currently see only way by transforming Collection's Models to aaData before grid initialization depending on columns definition.

    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.
  • allanallan Posts: 63,519Questions: 1Answers: 10,473 Site admin
    Actually what might be the best method of integration between DataTables and Backbone.js is using mDataProp as a function. That way you can pass in an array of objects (a collection) to DataTables and then either execute a view function for each column or pull in an object's property (I haven't used Backbone.js much myself, but I suspect the function execution would be needed in this case). From the mDataProp function for each column you just return the data you want. Note that this doesn't take account of updates, which would as you say need to bind a listener and redraw the table (a redraw should do in this case if mDataProp is a function since whenever DataTables needs to get data for a cell it will always do whatever mDataProp tells it to.

    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
  • benmxbenmx Posts: 1Questions: 0Answers: 0
    Hi *,

    any findings on the DT/Backbone.js integration? I would be interested as well!

    /Ben
  • allanallan Posts: 63,519Questions: 1Answers: 10,473 Site admin
    Not yet, but it is on my to-do list for this week. Was hoping today, but need to see how the day shapes up a bit!

    Allan
  • drewdadrewda Posts: 2Questions: 0Answers: 0
    Hello 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
  • allanallan Posts: 63,519Questions: 1Answers: 10,473 Site admin
    Hi 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
  • drewdadrewda Posts: 2Questions: 0Answers: 0
    Hi 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
  • allanallan Posts: 63,519Questions: 1Answers: 10,473 Site admin
    > 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?

    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
  • marcmarc Posts: 3Questions: 0Answers: 0
    I am also interested in using DataTables with Backbone. In our application, we have geo-located data on a server and need to display a filtered view of it in the table, a map view of the same filtered data (using OpenLayers), and edit individual items in another view (the table contains only a subset of the data for each item).

    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
  • jcreadyjcready Posts: 44Questions: 0Answers: 0
    I do love DataTables, but integrating Backbone.js into DataTables introduces a lot of overlapping functionality. If you're dead set on using Backbone.js you may want to look into using something like SlickGrid: https://github.com/mleibman/SlickGrid/wiki which is much less tightly coupled with rendering, sorting, and events. It's more focused on manipulating the underlying data.

    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.
  • marcmarc Posts: 3Questions: 0Answers: 0
    Thanks jcready. I did see references to SlickBack in some of the other postings and I will keep that option in mind. I think I will try to get something working with DataTables first though, since I am already somewhat familiar with its API and, as you note, the documentation, etc. are very important factors.
  • jcreadyjcready Posts: 44Questions: 0Answers: 0
    It is definitely possible to use Backbone's models and collections with DataTables, but the real issue is efficiency. Not only will you be storing the same data in numerous places, but you will also be updating each data source for any CRUD operations.

    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.
  • allanallan Posts: 63,519Questions: 1Answers: 10,473 Site admin
    With regard to using views with DataTables, I always thought it would be possible to use fnRowCallback to have a TR node rendered by a view put into the table, where DataTables wants it to go.

    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
  • marcmarc Posts: 3Questions: 0Answers: 0
    For this application, time-to-market is probably more important than optimal performance, as long as it is "fast-enough". I am pretty new to both DataTables and Backbone (and to the client side in general!), but my general concept is to have Backbone effectively provide the raw data and let data tables format it, basically analogous to using fnServerData.

    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]
  • bihebihe Posts: 1Questions: 0Answers: 0
    Do you have a full example including the html template for this? I'm just struggeling to get this working.
  • parljohnparljohn Posts: 1Questions: 0Answers: 0
    Hi,
    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.
  • allanallan Posts: 63,519Questions: 1Answers: 10,473 Site admin
    Agreed - I think that should be the goal with any work towards Backbone integration for DataTables - make use of the binding. I've done some work for 1.10 already which at least allows that to be achievable, although I've not actually tried an integration yet. If anyone else wants to give it a bash, the 1.10 dev code in GitHub has the required change (to not take an independent copy of the data used for the table).

    I'll be writing a blog post about how to do it post 1.10 release (aiming for February).

    Allan
  • zanedevzanedev Posts: 8Questions: 0Answers: 0
    hi allan, i'd like to see how you did this but I don't see the 1.10 dev code in github, only 1.9 release branch available and I pulled master and searched everywhere for backbone. any tips on how to get started testing this?
  • allanallan Posts: 63,519Questions: 1Answers: 10,473 Site admin
    There is no change specific for backbone - as I say, the change makes it achievable, while before it was impossible. The change is in _fnAddData, and it no longer clones the data passed in, it just uses the original data source. No further change should be needed in DataTables, but you'll likely need to use mData or mRender to get the information from the backbone item / collection.

    Allan
  • EranEran Posts: 26Questions: 0Answers: 0
    edited January 2013
    Any updates on this matter ?

    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.
  • allanallan Posts: 63,519Questions: 1Answers: 10,473 Site admin
    As I noted above, I've made the commit to the 1.10 development version that will not kill the binding, but I haven't done anything with actually experimenting with Backbone yet - not had a chance with everything that is going on. I'll give it a bash once 1.10 is in beta which should be sometime next month.

    Allan
  • jclyonsjclyons Posts: 1Questions: 0Answers: 0
    edited April 2013
    Allan, et. al,

    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
This discussion has been closed.