Search code examples
javascriptmustache

Mustache.js external template (without jQuery)


I'm writing a component without jQuery as a dependency, and I'm hoping to find a way to load an external Mustache.js template without jQuery. Using jQuery's $.get method appears to work, but I'm trying to do this in vanilla JS.

I tried using an XMLHttpRequest and appending the template to the body, and then hydrating it with my JSON, but when my JS tries to put the JSON in the template, the template isn't there to be hydrated (cannot read property innerHTML of null). Here was my code (in CoffeeScript, test.js is the mustache template):

req2 = new XMLHttpRequest()
req2.onload = ->
  foo = document.getElementById('thatsmyjam')
  templ = document.createTextNode(this.responseText)
  foo.insertAdjacentHTML('beforeEnd', templ)
req2.open('GET', 'test.js', { async: false})
req2.responseType = 'document'
req2.send()

This adds the literal text [object Text] in the DOM instead of treating it as HTML, so it seems to be evaluating the string of HTML rather than rendering it as such.

There's probably a better way. I'm basically trying to combine my App (getting the JSON), mustache.js, and the template into one concatenated, minified file for distribution as a UI widget.

I also looked into something like Hogan.js to precompile the template, but it felt complicated, and I'm not able to use Node in this project.

Update

If I update the above CoffeeScript to this:

req2 = new XMLHttpRequest()
req2.onload = ->
  foo = document.getElementById('thatsmyjam')
  window.templ = document.createTextNode(this.responseText)
  foo.insertAdjacentHTML('beforeEnd', templ)
req2.open('GET', 'test.js', { async: false})
req2.send()

then it's treated as a string in the relevant part of my app that tries to render the template:

populateDom: =>
    self = @
    @request.addEventListener 'loadend', ->
      if @status is 200 && @response
        resp = self.responseAsJSON(@response)
        # here, window.templ is a string returned from the XMLHttpRequest above,
        # as opposed to an actual "template", so Mustache can't call render with it.
        rendered = Mustache.render(window.templ, resp)
        document.getElementById('thatsmyjam').innerHTML = rendered
        self.reformatDate(resp)

So Mustache treats the string differently than a template inside of a script tag. Is there a way to get Mustache to recognize that string as a legitimate template?


Solution

  • I figured out how to retrieve an external template using core JavaScript using an implementation inspired by this SO answer. The process is, essentially, create a new div, retrieve the template with an XMLHttpRequest, and fill the created div's innerHTML with the template string. Here's the implementation in CoffeeScript:

    class TemplateManager
      templateUrl: '/path/to/template.mustache'
      retrieveTemplate: ->
        req = new XMLHttpRequest()
        req.onload = ->
          div = document.createElement('div')
          div.innerHTML = this.responseText
          window.mustacheTemplate = div
        req.open('GET', @templateUrl, { async: false})
        req.send()
    

    You can then call

    rendered = Mustache.render(window.mustacheTemplate.innerHTML, resp)
    document.getElementById('YOURDIV').innerHTML = rendered
    

    to render the template with resp, your JSON data.