Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone.js Link Model and VIew

I have a table like this

<table>
  <tr id="row-one" class="item-row">
    <td class="price"></td>
    <td><input type="text" class="quantity" value="" /></td>
  </tr>
  <tr id="row-two" class="item-row">
    <td class="price"></td>
    <td><input type="text" class="quantity" value="" /></td>
  </tr>
  <tr id="subtotal">
    <td id="subtotal"></td>
    <td id="subquantity"></td>
  </tr>
</table>

That I am manipulating with this backbone js code

<script type="text/javascript">
  jQuery(document).ready(function($){
    var Item = Backbone.Model.extend({
      initialize: function(){
      },
      defaults: {
        price: 0,
        quantity: 0
      }
    });
    var ItemsCollection = Backbone.Collection.extend({
      model : Item,
      subquantity: function(){
        subquantity = 0;
        this.each(function(item){
          subquantity += item.get('quantity');
        });
        return subquantity;
      }
    });
    var ItemView = Backbone.View.extend({
      events: {
        "change .quantity": "updateQuantity"
      },
      render: function(){
        alert('render');
      },
      updateQuantity: function(){
        this.model.set({quantity: this.$('.quantity').val()});
        console.log(this.model.get('quantity'));
      }
    });
    var totals = new ItemsCollection();
    var item_one = new Item({ price:1,quantity:10 });
    var item_two = new Item({ price:1,quantity:20 });
    totals.add(item_one);
    totals.add(item_two);
    var view_one = new ItemView({model:item_one,el:'.item-row'});
  })
</script>

My questions are.

1) So far I can manage one row, that's fine. But then I want to manage the second row. Do I just instantiate another instance of ItemView? Or can I use one ItemView instance to manage multiple Models? Or better yet, with the Collection

2) I am using the Collection to manage the subtotals and subquantity. Do I create a separate view to manage those fields in the DOM? Or should I just update them inside the collection?

If anyone has any other tips I would love to hear it. This is my first time with backbone, so I just want as much info as possible. Thanks!

like image 364
vinhboy Avatar asked Dec 10 '22 05:12

vinhboy


1 Answers

As with most Backbone questions, there isn't a "right" answer to this - you'll need to figure out the approach that works best for you, which will depend in part on how complex your application is likely to get.

The main thing that seems odd to me in your setup is that you've hard-coded your table to list two items - my guess is that, if you're using Backbone to begin with, you probably will be pulling a list of items from the server via AJAX and then populating the table with that list. In that case, usually you'd actually create the DOM for each row in the ItemView class:

var ItemView = Backbone.View.extend({
  // there are many different ways to do this, here's one
  tagName: 'tr',
  className: 'item-row',
  // make an Underscore template for the inner HTML
  template: _.template(
    '<td class="price"><%= price %></td>' +
        '<td><input type="text" class="quantity" value="<%= quantity %>" /></td>'
  ),
  render: function(){
    // this.el is already set to a tr.item-row DOM element
    $(this.el).html(
        // fill in the template with model attributes
        this.template(this.model.toJSON())
    );
    // allows chaining
    return this;
  }
  // ... now your events and handler as written
});

This gives you an ItemView which, when rendered, has the rendered element you want to insert available once you call .render(). Depending on your setup, you might call this.render() right away in an initialize() function, or you might fetch more data asynchronously and then call render() in the callback. Either way, you have one view instance tied to one model. To manage multiple models, I might have another View class to hold the collection, which would be responsible for instantiating child ItemView instances:

var ItemsView = Backbone.View.extend({
    el: '#myTable',
    render: function() {
        var tableView = this;
        // instantiate and render children
        this.collection.each(function(item) {
            var itemView = new ItemView({ model: item });
            $(tableView.el).append(itemView.render().el);
        });
    }
});

Then in the main body of your code, all you need to do is pass the collection to your parent view, and render it:

var tableView = new ItemsView({ collection: totals });

There's a lot more to add here, most of it dealing with asynchronously fetching, rendering, and refreshing data from the server, but hopefully this gives you a basic idea of the approach. Again, however, I should emphasize that this is just one way to do things - you could, for example, build the same app with only the ItemsView class, making it responsible for rendering all of the items itself. But the approach of using one view to manage the collection plus child views to manage the models keeps things nicely separate, and helps to minimize complexity as your app gets bigger.

like image 128
nrabinowitz Avatar answered Jan 01 '23 14:01

nrabinowitz