Search code examples
javascriptbackbone.js

How to bind Backbone.View.el to a template without a wrapper?


Here's a template I'm using

<script type="text/template" id="template-message">
  <p class="message"><%= message %></p>
</script>

Here's my Backbone.View

var MessageView = Backbone.View.extend({

  template: _.template(Backbone.$("#template-message").html()),

  render: function() {
    this.$el.html(this.template(this.model.attributes));
    return this;
  },
});

How I'm using it

var msg  = new Message({message: "hello"}),
    view = new MessageView({model: msg});

$("body").append(view.render().el);

Output

<body>
  <div>
    <p class="message">
      hello
    </p>
  </div>
</body>

I understand why it's happening (the default tagName for a view is div), but I don't want the wrapper div on there. I would like to see this as the output

<body>
  <p class="message">
    hello
  </p>
</body>

I'm aware that I could have this in my view

var MessageView = Backbone.View.exntend({
  tagName: "p",
  className: "message",
  // ...
});

The end user will be define the templates, so I don't want those things to be specified in the Backbone.View source code.

How can I get the desired output and still bind the view to the html?


Solution

  • Thanks to @Gohn67's comment, I was able to get this working

    var MessageView = Backbone.View.extend({
    
      template: _.template(Backbone.$("#template-message").html()),
    
      initialize: function() {
        this.setElement(this.template(this.model.attributes));
      }
    
    });
    

    If we take a look at the Backbone Source Code, we can see it setElement does everything we want it to do

    // backbone source code
    setElement: function(element, delegate) {
      if (this.$el) this.undelegateEvents();
      this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
      this.el = this.$el[0];
      if (delegate !== false) this.delegateEvents();
      return this;
    },
    

    It even handles the event delegation. Cool!


    I recently had to implement this on jQuery 1.3.2. Gross, I know, but it was a simple fix.

    The only part that changes is the setElement bit

      initialize: function() {
        this.setElement(
          $( this.template(this.model.attributes) )
        );
      }
    

    The template result must be wrapped in $() in order to work. I know, it doesn't make a lot of sense considering Backbone is already trying to do this automatically. It's the only thing I could do to make it work with jQuery 1.3.2 though.