Search code examples
ruby-on-railsbackbone.jshandlebars.js

Using Backbone and Handlebar to make a table with HTML in a row?


I have a Ruby on Rails app, using Backbone. In the coffee, I have:

class App.Views.TableElement extends Backbone.View
  events:
    "change input[name='past_results']": "togglePastResults",
    "change .cohort_toggle select": "selectCohort"

  initialize: (options) ->
    @listenTo(@model, "change", @render)

  render: =>
    console.log @model.toJSON()
    @$el.empty().append(HandlebarsTemplates['shared/table_element'](@model.toJSON(), data: { tableClass: @tableClass() }))
    @$el.find('.cohort_toggle select').val(@model.get("selectedCohortLabel"))
    @$el

I think that that's the relevant bit.

I'm not going to present the entire model.toJSON output, but it's got headers and rows, like a table should. One of the elements in one of the rows is supposed to be a link. Eventually I think it's going to be a link to javascript I think, but for now, I just want a link to google for testing... So I'm hardcoding the element as '<a href="https://google.com">Visit google</a>' However, that's getting interpreted with &lt and stuff, so the HTML is what actually appears in the table.

The questions are: Who's escaping my HTML, and how do I make it stop?

The model (parts of it):

class App.Table extends Backbone.Model
  defaults: {
    showPastResults: true
  }

  initialize: ->
    @set("selectedCohortLabel", @get("cohortLabels")[0])


  columns: =>
    columns = []
    Array::push.apply columns, [@get('categories')]
    Array::push.apply columns, @get('currentData')

    Array::push.apply columns, [['<a href="https://google.com">Visit google</a>', 5, 5]]    if @get('haveDataBytes')
    columns

template. All of it because I'm not sure what any of it does... and so it's got columns that the model excerpt doesn't:

<table class="table {{@tableClass}}">
  <thead>
    <tr>
      {{#each headers}}
      <th class="{{this.class}}">
        {{#if this.data }}
          {{ this.data }}
        {{ else }}
          {{ this }}
        {{/if}}
      </th>
      {{/each}}
    </tr>
  </thead>

  <tbody>
    {{#each rows}}
    <tr>
      {{#each this}}
      <td>{{this}}</td>
      {{/each}}
    </tr>
    {{/each}}
  </tbody>
</table>

Solution

  • From the fine manual:

    HTML Escaping
    Handlebars HTML-escapes values returned by a {{expression}}. If you don't want Handlebars to escape a value, use the "triple-stash", {{{.

    Presumably your columns data gets into the model's toJSON return value somewhere. Your template will need some way of knowing when it is dealing with a link so that it can say {{{link}}} instead of {{link}} to keep {{...}} from escaping the HTML.


    Also, you could write your columns method like this:

    columns: ->
      columns = [ @get('categories'), @get('currentData')... ]
      if @get('haveDataBytes')
        columns.push ['<a href="https://google.com">Visit google</a>', 5, 5]
      columns
    

    to avoid the strange and non-idiomatic use of Array's prototype.