0
votes

I'm trying to create a Backbone Model (lets call it Library) which contains a Collection of other Models (lets call them Books). I am providing a view - LibraryView, which creates an HTML of a book case with a set of books represented by HTML generated by BookView. Also, I am using Handlebars.js as my templating system.

The issue I am experiencing is that my BookView returns weird html on this.el element, before I even pass it through render() function.

LibraryModel Model

  var LibraryModel = Backbone.Model.extend({

    initialize: function() {

      var books = new BookCollection();

      _.each(book_data.books, function(value, index) {
        books.add(new Book());
      });

      this.books = books;

    }
  });

LibraryView View:

 var LibraryView = Backbone.View.extend({

    el: "#library",

    render: function() {

      var t = this;

      this.model.books.each(function(book, index) {
        //create new view for each Book model in the books collection
        var view = new BookView(book);
        //append HTML produced by the BookView into LibraryView el element
        t.$el.append(view.render().$el);
      });

      return this;  
    },

    initialize: function() {
      //snip
    }
  });

BookView View:

  var BookView = Backbone.View.extend({

    render: function() {
      var viewmodel = this.model;
      var source = $("#book-template").html();
      var template = Handlebars.compile(source);
      var html = template(viewmodel.toJSON());

      console.log(html); //prints <div test="wtf" anotherTest="123"><b>wtf</b> 123</div>
      this.$el.html(html);

      return this;  
    },

    initialize: function(book) {
      console.log(this.el.outerHTML); //prints <div test="wtf" anotherTest="123"></div>
      this.model = book;
      this.listenTo(this.model, "change", this.render);
    }
  });

Template I am providing is: <b>{{test}}</b> {{anotherTest}}

BookModel Model

  var BookModel = Backbone.Model.extend({
    defaults: {
      test: "wtf",
      anotherTest: 123
    },

    initialize: function() {
        //snip
    }
  });

Basically, the issue I am exeperiencing is that my BookView produces weird HTML where each of my model attributes is attached to the Backbone-generated div, like this:

<div test="wtf" anotherTest="123">
<b>wtf</b> 123
</div>

I am not setting any of the attributes anywhere else in the code - both values are only coming from the defaults.

Also, I confirmed this is not something that Handlebars is doing, as Model-attributes are inserted as HTML-atributes into Backbone generated div of the BookView model (note, I am not providing tagName or el manually, I want Backbone to create a div for me).

So here is where I am stuck. I have a perfectly working list of HTML generated by BookView for each of my models in the list, but for some reason Backbone-generated div wrapper contains each of Model-attributes in its HTML-attributes, like so:

<div id="#library">
<div test="wtf" anotherTest="123"><b>wtf</b> 123</div>
<div test="wtf" anotherTest="123"><b>wtf</b> 123</div>
<div test="wtf" anotherTest="123"><b>wtf</b> 123</div>
</div>

I am really pulling my hair out over this and I have suspicion it has something to do with the fact I am trying to use View-in-a-View.

Have you encountered similar problems before? Have you got good examples of Backbone application where MasterView renders collection of ChildViews?

1

1 Answers

2
votes

You are doing a weird thing when creating new BookViews. That view is expecting a BookModel to come in its initialization. However, the initialize() method always expects an object which is called attributes by convention. You should modify your BookView so it matches this:

var BookView = Backbone.View.extend({

    render: function() {
       // keep the same code
    },

    initialize: function(attributes) {
        this.listenTo(this.model, "change", this.render);
    }
});

and in your LibraryView you should use:

var view = new BookView({ model : book });

instead of the previous:

var view = new BookView(film); // which is a typo, and should pass book and not film

So now you get your expected result.

<div id="library">
    <div><b>wtf</b> 123</div>
    <div><b>wtf</b> 123</div>
    <div><b>wtf</b> 123</div>
</div>

Keep in mind that initialize(attributes) will automatically call set with that mapping, so you don't have to call set(attr, value) yourself. With that in mind, we can understand why that view is having two attributes which are in fact your two model's attributes (they are being set() on initialization).