TryGhost / express-hbs

Express handlebars template engine with inheritance, partials, i18n and async helpers.
MIT License
458 stars 76 forks source link

Inline-partials helper to fill slots? #257

Open gnimmelf opened 9 months ago

gnimmelf commented 9 months ago

Hi guys,

Using inline partials to fill up slots in another partial is a fantastic feature when developing themes. See https://handlebarsjs.com/guide/partials.html#inline-partials

However slots that are empty by default must be filled to avoid getting a missing-partial error: (See handlebars repo here and here.)

Example:

/partials/templates/article.hbs:

<div>
  {{>empty-by-default-top-slot}}

  {{#>filled-by-default-slot}}    
    {{! Some widget or whatever}}    
  {{/filled-by-default-slot}}

  {{>content_loop_or_whatever}}

  {{>empty-by-default-end-slot}}
</div>

If I do not want to pass content to some slots, I need to pass an empty partial.

/page.hbs:

{{>nav}}

{{#>templates/article}}
    {{#*inline "empty-by-default-top-slot"}}{{/inline}}
    {{#*inline "empty-by-default-end-slot"}}{{/inline}}
{{/templates/article}}

{{>footer}}

A theme using inline partials can have many optional slots. This makes the templates overly verbose and cumbersome, almost on par with passing bool-variables to the template to if-test. However a helper to check if the partial is defined would simplify the process by either passing content or not, like so:

{{>templates/article}}

There is a stalled pull for this in handlebars: https://github.com/handlebars-lang/handlebars.js/pull/1953

And here is the ghost rewrite I currently use:

// # Partial helper to test if inline partials are defined in scope.
// 
// Usage: `{{#if (is_partial_defined "slot-name"}}{{>slot-name}}{{/if}}`
//
// Returns true if a partial is defined, otherwise false
const instance = require("../services/theme-engine/engine");
const errors = require('@tryghost/errors');
const tpl = require('@tryghost/tpl');

const messages = {
  partialNameMissing:
    "The {{is_partial_defined}} helper must be passed a partial name",
};

module.exports = function is_partial_defined(partialName, options) {  
    if (!partialName) {
      throw new errors.IncorrectUsageError({
        message: tpl(messages.partialNameMissing),
      });
    }

    if (instance.handlebars.partials[partialName] === undefined) {
      return false;
    } else {
      return true;
    }
};

I know you have a strict policy on custom expansions across the board, which is appreciated, but I am kind of hoping for this one, as it's more of a "core" theme-developer-geared thing. You decide! =)

Thanks, Flemming