Search code examples
knockout.jshandlebars.jsmustachenunjucks

Mustache-like language with knockout-observables


I am trying to use a Knockout-style view-model with a mustache-like language.

So the view model looks something like this:

var viewModel = {
  aValue: ko.observable("boot")
}

The rather common problem with mustache-like languages is that they do not unwrap the models. So one gets e.g.

mustache_lib.render("{{ aValue }}", viewModel)
   => "function (initialvalue) { function observable() { ..."

That's what happens with Nunjucks, among others (with their respective render function).

One could use a filter with Nunjucks, so the following works as expected:

nunjucks.renderString("{{ aValue | unwrap }}", viewModel)
   => "boot"

Where unwrap is a filter, being ko.unwrap. However I am not a fan of the syntax, and I was wondering if there were a better option. I perused Nunjucks code and it did not seem possible to automatically unwrap every variable looked up in a context.

Handlebars.js has a preferable syntax (in my eyes), e.g.

Handlebars.compile("{{ unwrap aValue }}")(viewModel)
   => "boot"

Unfortunately if you forget the unwrap, then aValue will be treated as a helper and called with an undesirable value, something like { data: Object, hash: '', name: '...' }. Obviously one prefers not to have their templating language do this. Similar to Nunjucks, Handlebars does not have a way to modify the context lookups to automatically call ko.unwrap.

Interestingly, Mustache.js will call any variable that is a function (including, of course, observables). However it lacks a number of features in other libraries.

Has anyone had any experience with using Mustache-like libraries with Knockout and worked through the relationship (including which mustache libraries are most suitable)?


Solution

  • FWIW, I ended up using dust.js, and making ko.subscribable's thenable by adding a then function to ko.subscribable.fn (with then resolution being prioritized in the next release of dust.js).