2

I currently use handlebars in java (com.github.jknack.handlebars) and have a handlebars helper method to get a link (with some logic behind)

{{ getLink data.node}}

which just render the url /subfolder/link.html

In my handlebars template I now only want to print the url (a-tag) when the helper method returns a non empty string (e.g. there is a link available)

I tried it with

{{#if getLink data.node }}
     <a href="{{ getLink data.node}}">Link-Text</a>
{{/if}}

but no link was rendered

What would be the correct syntax for my if?

Thanks RCX

Edit: getLink Helper Method in LinkHelper.class

public CharSequence getLink(JsonObject node, Options options) throws IOException {
    String link = fetchLink(node);
    if(link != null){
        return link;
    }
    return "";
}

registered via

    HandlebarsTemplateEngineImpl.getHandlebars().registerHelpers(new LinkHelper());
RCX
  • 155
  • 3
  • 17

1 Answers1

2

Handlebars.java is not well documented and missing couple of unit tests (consider to contribute if this answer helped), for some reason calling nested JsonObject removed at this commit, you can still call nested String, but there is a trick with parentheses.

Full example:

import com.github.jknack.handlebars.Context;
import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Helper;
import com.github.jknack.handlebars.Template;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Map;

public class HandlebarsJavaTest {

    public static void main(String[] args) throws IOException {
        Handlebars handlebars = new Handlebars();
        Gson gson = new Gson();

        handlebars.registerHelper("getLink", (Helper<Map<String, Object>>) (jsonObject, options) -> {
            String link = fetchLink(jsonObject);
            return link != null ? link : "";
        });

        String data = "{'data':{'node':'/bla.html', 'node2':'inside node2'}}";
        // Pay attention to parentheses !!!
        // {{#if (getLink data.node)}} throws ClassCastException, java.lang.String cannot be cast to java.util.Map
        String rawTemplate = "{{#if (getLink data)}} <a href=\"{{getLink data}}\">Link-Text</a> {{/if}}";

        Type type = new TypeToken<Map<String, Object>>(){}.getType();
        Map<String, Object> map = gson.fromJson(data, type);

        Template template = handlebars.compileInline(rawTemplate);
        Context context = Context.newBuilder(map).build();

        System.out.println(template.apply(context));
    }

    private static String fetchLink(Map<String, Object> map) {
        try {
            return map.get("node").toString();
        } catch (NullPointerException npe) {
            return null;
        }
    }
}

Output:

<a href="/bla.html">Link-Text</a>

If node is just a string ( same output )

public static void main(String[] args) throws IOException {
    Handlebars handlebars = new Handlebars();
    Gson gson = new Gson();

    handlebars.registerHelper("getLink", (Helper<String>) (node, options) -> node != null ? node : "");

    String data = "{'data':{'node':'/bla.html', 'node2':'inside node2'}}";
    // Pay attention to parentheses !!!
    String rawTemplate = "{{#if (getLink data.node)}} <a href=\"{{getLink data.node}}\">Link-Text</a> {{/if}}";

    Type type = new TypeToken<Map<String, Object>>(){}.getType();
    Map<String, Object> map = gson.fromJson(data, type);

    Template template = handlebars.compileInline(rawTemplate);
    Context context = Context.newBuilder(map).build();

    System.out.println(template.apply(context));
}

If you insist node is an object using this.[data] or this.[data.node] will not do the work, working example for the same output:

import com.github.jknack.handlebars.Context;
import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Helper;
import com.google.gson.Gson;
import com.google.gson.JsonObject;

public class HandlebarsJavaTest {

    public static void main(String[] args) throws Exception {
        System.out.println(
            new Handlebars()
                .registerHelper("getLink", (Helper<JsonObject>) (json, options) -> {
                    try {
                        // logic here
                        return json.get("data").getAsJsonObject().get("node").getAsJsonObject().get("link").getAsString();
                    } catch (NullPointerException npe) {
                        return null;
                    }
                })
                // Pay attention to parentheses !!
                .compileInline("{{#if (getLink this) }} <a href=\"{{getLink this}}\">Link-Text</a> {{/if}}")
                .apply(
                    Context
                        .newBuilder(
                            new Gson()
                                .fromJson(
                                    "{ 'data': { 'node': { 'link': '/bla.html' }, 'node2': 'inside node2' } }",
                                    JsonObject.class
                                )
                        ).build()
                )
        );
    }
}

If you have time, consider to contribute to this open source by adding proper documentation or at least unit tests {{#if (method param)}}.

According to the source code

package com.github.jknack.handlebars;

public class IfBlockTest extends AbstractTest {

@Test
public void falsy() throws IOException {
    // empty string
    shouldCompileTo("{{#if value}}true{{else}}false{{/if}}", $("value", ""), "false");

BTW, the #if built-in helper will return False for empty string, so if getLink will execute return ""; the if condition will not and the text will not be rendered, to assert this you can add {{else}} before the closing if {{/if}} and see that what is rendered.

whoopdedoo
  • 2,815
  • 23
  • 46
  • you can say ```String link = "/bla.html";``` but the problem is the link is never displayed (it doesnt matter if i return "", null, or "/bla.html" :( the if always evaluates to false – RCX Mar 28 '18 at 15:44
  • omg - thanks a lot - it was just the missing parentheses around the helper call ```{{#if (getLink this) }} ``` – RCX Mar 29 '18 at 08:27