Simulate null parameters in Freemarker macros

那年仲夏 提交于 2019-11-30 12:24:15

The null reference is by design an error in FreeMarker. Defining a custom null value - which is a string - is not a good idea for the reasons you mention. The following constructs should be used instead:

  • Macro and function parameters can have a default value, so the callers can omit them
  • To check if a variable is null, you should use the ?? operator: <#if (name??)>
  • When you use a variable that can be null, you should use the ! operator to specify a default value: name!"No name"
  • To check if a sequence (or a string) is empty, use the ?has_content builtin: <#if (names?has_content)>

You can specify an empty sequence as default parameter value in a macro, and simply test whether it's empty.

Here's what I did, which seems to work in most scenarios:

The default value should be an empty string, and the null-check should be ?has_content.

<#macro someMacro optionalParam="" >
    <#if (optionalParam?has_content)>
        <#-- NOT NULL -->
    <#else>
        <#-- NULL -->
    </#if>
</#macro>

A coworker has suggested a null-checking function may be the most elegant solution:

<#assign null="NUL" />

<#function is_null variable>
  <#if variable?is_string & variable == null>
    <#return true />
  <#else>
    <#return false />
  </#if>
</#function>

Running with that idea, I found another possible solution in the mailing list, which is to create a null-checking function in Java by extending TemplateMethodModelEx. I can then insert it directly into my model map and have it globally available in all my templates. The resulting Freemarker code is pretty clean:

<#maco doSomething param1=null>
  <#if !is_null(param1)>
    <div>WIN!</div>
  </#if>
</#macro>

This seems to be the best way to simulate null parameters inside a macro.

You can check if it's not a scalar first:

<#if !param1?is_scalar || param1 != null)>

This is another solution that may fit to your need.

<#macro el user={}>
...
<input type="text" class="form-control" name="firstName"
                                       value="${(user.identity.firstName)!''}">
...
</#macro>

I define a default empty object {} instead of null to avoid 2 big if else block in my element.

make sure that when you give a default value to the attribute (firstName in my case), to put the parenthesis to all the expression then the ! sign.

So whatever identity is undefined or firstName is undefined, freemarker use the default value

(user.identity.firstName)!''

Actually i do not understand why null's are not allowed. I have a java function to retrieve a specific image URL with given optional / nullable parameters like height.

I wrote a macro for it using following workaround:

Java function

/**
 * Converts the given value to <code>null</code> if it equals the default
 * value, else returns it directly.
 *
 * @param value
 * @param defaultValue
 *
 * @return
 */
public static Object nullIfDefault(final Object value, final Object defaultValue)
{
    if (value != null && value.equals(defaultValue))
        return null;
    return value;
}

/**
 * Converts the given value to <code>null</code> if it equals an empty
 * string, else returns it directly.
 *
 * @param value
 *
 * @return
 */
public static Object nullIfEmpty(final Object value)
{
    return ObjectUtils.nullIfDefault(value, "");
}

Macro

Import static function

<#assign ObjectUtils=statics['com.example.ObjectUtils']/>

<#macro compEntityImage imageRetriever entity height="" width="">

    <img src="${imageRetriever.getMediaFileUrl(entity, 
        ObjectUtils.nullIfEmpty(height), 
        ObjectUtils.nullIfEmpty(width))}">

</#macro>

Markup

<@compEntityImgage imageRetriever=imageRetriver entity=product height=500/>
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!