Search code examples
backbone.jsunderscore.jsjasmine

Cannot get variables to interpolate in underscore.js template while jasmine.js testing


EDIT: Forgot to remind the reader that I remembered to set templateSettings as follows:

_.templateSettings = {
    interpolate : /\{\{([\s\S]+?)\}\}/g
};

I'm having a hard time getting a varialbe to interpolate in underscore, while running my Jasmine specs. Given the template, rendering method, and jasmine test below, I am able to get the template to interpolate variables properly via:

_.template(
  boneHeaderInstance.template.html(), 
  { id:boneHeaderInstance.id,  
    columns:boneHeaderInstance.columns
  }
)

While this fails to interpolate the columns variable:

boneHeader = Backbone.View.extend({
  el: $('#boneTableHeader'),
  template: $('#boneTableHeaderTemplate'),
  initialize: function(){
    this.id = 'boneTableHeader';
    this.el = $( '#' + this.id );
    this.columns = 'blah';
    this.template = $( '#' + this.id + 'Template' );
    this.render();
    return this;
  },
  render: function(){
    var that = this;
    var data = {id: that.id, columns: that.columns}
    this.el.html( _.template( this.template.html(), data ) );
  }
});

Template:

<script type = 'text/template' id = 'boneTableHeaderTemplate'>
  <tr id = "{{obj.id}}Row">
    {{obj.columns}}
  </tr>
</script> 

In Render Method:

render: function(){
  var that = this;
  var data = {id: that.id, columns: that.columns}
  this.el.html( _.template( that.template.html(), data ) );
}

Jasmine Test:

describe('boneHeader', function(){
  beforeEach(function(){
    boneHeaderInstance = boneTableInstance.header;
  }); 
  describe('rendering', function(){
    it('should have expected html', function(){
      expect( 
        boneHeaderInstance.el.html().replace(/\s\t\n/ , '', 'g') 
      ).toEqual( 
        _.template(boneHeaderInstance.template.html(), 
        { id:boneHeaderInstance.id,  
          columns:boneHeaderInstance.columns
        }).replace(/\s\t\n/ , '', 'g') 
      );
    }); 
  }); 
});

Jasmine Result:

Expected ' <tr id="boneTableHeaderRow"></tr> ' to equal ' <tr id = "boneTableHeaderRow"> blah </tr> '

Solution

  • You have various problems. First of all, Underscore uses <% %> for templates unless you change it with something like:

    _.templateSettings = {
      interpolate : /\{\{(.+?)\}\}/g
    };
    

    So your template should look like this:

    <script type = 'text/template' id = 'boneTableHeaderTemplate'>
        <tr id = "<%= obj.id %>Row">
            <td><%= obj.columns %></td>
        </tr>
    </script>
    

    I've also fixed the HTML error you had in your template, you can't have a text node as an immediate child of a <tr> and there's no telling what sort of chicanery a browser will get up to if you try such a thing.

    Secondly, _.template() is usually used to return a compiled version of a template and that compiled version is a function that you execute to get the final HTML:

    var t    = _.template(some_template_html);
    var html = t(data);
    

    So you probably want something like this in your constructor:

    this.template = _.template($('#' + this.id + 'Template').html());
    

    and this in your render:

    this.el.html(this.template(data));
    

    You can do it all at once with _.template(template_html, context) though.

    Thirdly, you're referencing obj.id and obj.columns in your template but you're only giving it id and columns so either drop the obj. prefixes from your template or alter data thusly:

    var data = {
        obj: {
            id: that.id,
            columns: that.columns
        }
    };
    

    Demo: http://jsfiddle.net/ambiguous/NYLqH/

    You'll have to fix your test to account for the corrected HTML of course.