Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unknown Template Variables #1477

Closed
dominicporteous opened this issue Nov 11, 2018 · 4 comments
Closed

Unknown Template Variables #1477

dominicporteous opened this issue Nov 11, 2018 · 4 comments

Comments

@dominicporteous
Copy link

dominicporteous commented Nov 11, 2018

Is there a way to preserve the templated variable if a variable is undefined in the compile context?

e.g.

Hi, Hello {{ name }}! Its a {{ mood }} day.

Compiled with the following context;

{name: "Dom"}

Would result in the following output;

Hi, Hello Dom! Its a {{ mood }} day.

I've had a look through the api, and even some of the code, but I cant seem to find what I'm looking for.

The mustache docs state the following;

By default a variable "miss" returns an empty string. This can usually be configured in your Mustache library. The Ruby version of Mustache supports raising an exception in this situation, for instance.

http://mustache.github.io/mustache.5.html

I cant seem to find the place to configure this in Handlebars. Any help would be much appreciated!

@mcintyre94
Copy link

If your variables are flat (no nesting), then this workaround works:

// If a variable x is missing, put {{x}} in, ie retain it as a variable
handlebars.registerHelper('helperMissing', function({ name }) {
    return new handlebars.SafeString(`{{${name}}}`);
});

When your template is compiled with that context, it'll call helperMissing({name: 'mood', ...}) automatically (see http://handlebarsjs.com/builtin_helpers.html#helperMissing), so that implementation just returns it {{mood}} for that case.

Note that it only works with top-level variables though, so if you had {{user.name}} and {{weather.mood}} that wouldn't work unfortunately.

@EricMCornelius
Copy link

@dominicporteous @mcintyre94 Just ran into this issue as well. I've got a workaround (HACK) to handle arbitrary nesting via ES6 Proxy objects for the interim:

const hbs = require('handlebars');

const handler = path => ({
  get(target, prop, receiver) {
    if (typeof prop !== 'string') return;

    const toString = () => `{{{${path}}}}`;
    if (prop === 'toString') return toString;
    if (prop === 'toJSON') return toString;
    if (prop === 'toHTML') return toString;

    const full_path = path !== '' ? `${path}.${prop}` : prop;
    const new_target = target[prop];

    if (new_target === undefined) return context_proxy({}, full_path);
    if (typeof new_target === 'object') return context_proxy(new_target, full_path);
    return new_target;
  }
});

const context_proxy = (obj, path = '') => new Proxy(obj, handler(path));

const template_ = hbs.compile('Hi, Hello {{ data.name }}! Its a {{ data.mood }} day.');
const context = context_proxy({
  data: {
    name: 'Eric'
  }
});

const res = template_(context);

console.log(res);
Hi, Hello Eric! Its a {{{data.mood}}} day.

@nknapp
Copy link
Collaborator

nknapp commented Apr 5, 2020

Closing due to inactivity, but this may be related to #1435

@hkurra
Copy link

hkurra commented Jan 23, 2022

@dominicporteous @mcintyre94 Just ran into this issue as well. I've got a workaround (HACK) to handle arbitrary nesting via ES6 Proxy objects for the interim:

const hbs = require('handlebars');

const handler = path => ({
  get(target, prop, receiver) {
    if (typeof prop !== 'string') return;

    const toString = () => `{{{${path}}}}`;
    if (prop === 'toString') return toString;
    if (prop === 'toJSON') return toString;
    if (prop === 'toHTML') return toString;

    const full_path = path !== '' ? `${path}.${prop}` : prop;
    const new_target = target[prop];

    if (new_target === undefined) return context_proxy({}, full_path);
    if (typeof new_target === 'object') return context_proxy(new_target, full_path);
    return new_target;
  }
});

const context_proxy = (obj, path = '') => new Proxy(obj, handler(path));

const template_ = hbs.compile('Hi, Hello {{ data.name }}! Its a {{ data.mood }} day.');
const context = context_proxy({
  data: {
    name: 'Eric'
  }
});

const res = template_(context);

console.log(res);
Hi, Hello Eric! Its a {{{data.mood}}} day.

It is really good solution, thanks for providing this solution.
how to handle block level Helper like {{#each}}?
blockHelperMissing hooks works only if name matches a property in the current evaluation context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants