sunng87 / handlebars-rust

Rust templating with Handlebars

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

compile string to template without registering

dexterdy opened this issue · comments

Currently when I have a string in my helper that I want to transform into a template to render, I have to create a new instance of Handebars, register a template and then get the Template. I can't use the existing instance, because I don't have mutable access to it. This is a little verbose and seems unnecessary. It would be really nice if I could just use the existing instance and compile my string into a Template without registering it at all.

I have seen a previous issue with a similar question, so let me lay out my use case:

Some parts of my handlebars file has to be rendered on the client. It's rather simple to put a raw helper and a <template> tag around those parts, so that is what I did. However some parts have to be renderable on both the client and the server. The simple approach is to include a copy inside of a raw helper and a <template tag>, but I don't like that approach, because I don't like copies. Instead, I want to make a helper that does that for me. In this helper, I need access to the raw string representation of that piece of the handlebars template and simply write that back out inside a <template> tag (simple enough), but I also need to actually render the same piece of the template as well. My current solution is to register that string template inside of a new, dummy instance of handlebars and then get that template out again to call the render function on it.

That it why this functionality would be nice to have, at least in the helper functions.

@dexterdy it is possible to create a global registry and use render_template to render template string without registering.

Yes, I knew about this, however this requires me to select give it the right context. It's simple when it is the top level context, because I have simple access to that in the helper function, however when the context is supposed to be a block context (like in an each block), this is a little more complicated. Can I get access to the block context for rendering with render_template?

Can you give me an example of your template and data you want to render?

It sounds like you may be able do it with a custom helper

the template:

{{#each categories}}
  <div>
    {{{{bothSides "portfolio-articles-template-{{@index}}"}}}}
      {{#each articles}}
        <div class="portfolio-article">
          <span> {{title}} </span>
        </div>
      {{/each}}
    {{{{/bothSides}}}}
  </div>
{{/each}}

the result I want:

<div>
  <template id="portfolio-articles-template-0">
     {{#each articles}}
       <div class="portfolio-article">
         <span> {{title}} </span>
       </div>
     {{/each}}
  </template>
  <div class="portfolio-article">
    <span>title</span>
  </div>
</div>

the helper I wrote:

fn both_sides<'reg, 'rc>(
    h: &Helper<'reg, 'rc>,
    r: &'reg Handlebars<'reg>,
    ctx: &'rc Context,
    rc: &mut RenderContext<'reg, 'rc>,
    out: &mut dyn Output,
) -> HelperResult {
    let id = match h.param(0) {
        Some(json) => Some(
            json.value()
                .as_str()
                .ok_or(RenderError::new("id must be a string"))?,
        ),
        None => None,
    };
    let template = h.template();

    match template {
        Some(template) => {
            if template.elements.len() > 1 {
                return Err(RenderError::new("Can only be used as raw block: {{{{}}}}"));
            }

            let client_template = encase_with_template_tag(template.clone(), id);
            client_template.render(&r.clone(), ctx, &mut rc.clone(), out)?;

            let inner_string = match &template.elements[0] {
                TemplateElement::RawString(string) => string,
                _ => {
                    return Err(RenderError::new("Can only be used as raw block: {{{{}}}}"));
                }
            };
            let mut empy_handlebars = Handlebars::new();
            empy_handlebars.register_template_string("currentBothSides", inner_string)?;
            let server_template = empy_handlebars.get_template("currentBothSides").unwrap();
            server_template.render(&r.clone(), ctx, &mut rc.clone(), out)?;

            Ok(())
        }
        None => Ok(()),
    }
}

the data:

{
    "categories": [
        {
            "articles": [
                {
                    "body": "bunch of test words",
                    "subtitle": "test",
                    "title": "test",
                },
            ],
            "name": "other works",
        },
    ],
}