Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone.js - instantiate Models/Views from exisiting html

I've started looking at backbone.js today as a way to better organize the code in my application.

I was wondering (conceptually - so reply with pseudocode by all means) how I would use my existing html to create Backbone Models (and Views).

All of the tutorials I've found consist of using a blank html template and then injecting in the content using ajax. I don't want to do this.

If I have a collection of books.

<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta charset="UTF-8">
    <title>My Book Collection</title>
</head>
<body>
    <head>

    </head>
    <body>
        <ul id="bookCollection">
            <li class="book" data-book-id="1"><input type="text" name="book_1_name" value="My Book A"/></li>
            <li class="book" data-book-id="2"><input type="text" name="book_2_name" value="My Book B"/></li>
            <li class="book" data-book-id="3"><input type="text" name="book_3_name" value="My Book C"/></li>
            <li class="book" data-book-id="4"><input type="text" name="book_4_name" value="My Book D"/></li>
            <li class="book" data-book-id="5"><input type="text" name="book_5_name" value="My Book E"/></li>
        </ul>
    </body>
</body>
</html>

At this stage I would like to start managing each book as a model, calling a function whenever book names are changed (just an alert in the function for proof of concept), and then calling a URL to sync changes to the model with my database.

Can anyone point me in the right direction for a way to do the above using existing html on a page?

If it makes a difference I plan on using mustache for my templating.

like image 356
calumbrodie Avatar asked Jul 21 '11 11:07

calumbrodie


3 Answers

I was really trying to do the same thing as well and just found my way around it!

Was trying to build on a todo list example where I'd already have some todos on the page, want to bring them as models in my Todos collection and have them managed the same way it happens for elements that were added to a blank page.

The whole js code is pasted as a gist there https://gist.github.com/1255736 with comments to explain more.

The important part is on how to instantiate the collection. Basically:

  1. you get your existing html elements through jQuery. If your model's view is based on a tagName: 'li', then this is the kind of tag you need to retrieve here.
  2. You iterate through these tags to scrape the data that's in there that constitutes your models and create your models
  3. You create a view for each model, passing it the model and the base element. This was the problem I had: I was creating the view and only then trying to add it later through my_view.el = xxx. This doesn't work.
  4. You add your model to the collection

Note: the collection is usually tied to a view later so that using collection.add will update the view as well. Since initialize is called in the constructor, the collection has not been bound yet and you won't duplicate elements in your HTML by adding them here.

// this is the important part for initializing from html!
khepin.Todos = Backbone.Collection.extend({
    model: khepin.Todo,

    // In this function we populate the list with existing html elements
    initialize: function() {
        _.each(
            // get all the <li></li> todo items (the base for the todo view)
            // and for each of them:
            $('.todo'),
            function(a){
                // Create the model
                var todo = new khepin.Todo();
                // Find the todo's text
                var task = $(a).find('span')[0];
                task = $(task).text();
                // set the model correctly
                todo.set({
                    task: task
                });
                // create the todo view
                var todoView = new khepin.TodoView({
                    model: todo,
                    el: a // the el has to be set here. I first tried calling new TodoView and setting the 'el' afterwards
                    // and the view wasn't managed properly. We set the "el' to be the <li></li> we got from jQuery
            });
        // Add this new model to the collection
        this.add(todo);
        },
        this
    );
}
})

Hope this helps!

like image 191
Khepin Avatar answered Oct 18 '22 09:10

Khepin


Backbone's views are always bound to a specfic html element (the view's attribute el). You could have something like a BookCollectionView bound to ul#bookCollection and a BookView bound to li.book, which should be fine with your current template structure.

You can map a Book model to a view using a url to the model. If the model is fetched from that url and you have defined an event binding for the model change, the according view should refresh with the new model data. Same applies for the collection's url and a collection of books.

There are not many good tutorials on backbone out i guess, but study something like http://liquidmedia.ca/blog/2011/02/backbone-js-part-3/ or http://www.jamesyu.org/2011/02/09/backbone.js-tutorial-with-rails-part-2/. Guess it is easier if you can come up with some more concrete questions!

like image 6
Bernhard Vallant Avatar answered Oct 18 '22 11:10

Bernhard Vallant


I had the same problem and solved it this way within the constructor of my main view (the ul list) - as of backbone 0.9.

I converted it in my mind from coffeescript syntax so please be gentle if there are some syntax errors.

myListView = Backbone.View.extend({
    initialize: function() {
        ._each(this.$el.children(), function(book, i) {
             new Backbone.View({
                  el: book,
                  model: this.collection.at(i)
             });
        });
    }
});

and calling it like this:

new myListView({
     collection: anExistingCollection,
     el: $('#bookCollection')
});

It's important that the order of the collection 'anExistingCollection' is the same as your already generated list entries as this example relies on the same index.

(untested)

like image 2
acme Avatar answered Oct 18 '22 10:10

acme