Имитировать нулевые параметры в макросах Freemarker

Я создаю сайт, используя Freemarker, и начал активно использовать макросы. Я знаю, что в Freemarker 2.3 передача нулевого значения в макрос в качестве параметра эквивалентна тому, что я вообще не передавал параметр, поэтому я создал глобальную переменную с именем "null", чтобы моделировать проверку нуля в моих макросах:

<#assign null="NUL" />

Теперь в моих макросах я могу это сделать:

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

Проблема возникает, если я хочу передать параметр, который не является скаляром. Например, передача списка (который во Freemarker является SimpleSequence) макросу и проверка на мое ключевое слово null приводит к ошибке:

freemarker.template.TemplateException: Единственные юридические сравнения между два числа, две строки или две даты. Левый операнд - это freemarker.template.SimpleSequence Правый операнд - это freemarker.template.SimpleScalar

Я взглянул на код freemarker, и я вижу проблему (ComparisonExpression.isTrue()):

if(ltm instanceof TemplateNumberModel && rtm instanceof TemplateNumberModel) { 
  ...
}
else if(ltm instanceof TemplateDateModel && rtm instanceof TemplateDateModel) {
  ...
}
else if(ltm instanceof TemplateScalarModel && rtm instanceof TemplateScalarModel) {
  ...
}
else if(ltm instanceof TemplateBooleanModel && rtm instanceof TemplateBooleanModel) {
  ...
}
// Here we handle compatibility issues
else if(env.isClassicCompatible()) {
  ...
}
else {
  throw new TemplateException("The only legal comparisons...", env);
}

Итак, единственное решение, о котором я могу думать, это установить isClassicCompatible в true, который, как я думаю, вызовет toString() для обоих объектов и сравните результат. Однако в документации конкретно говорится, что все, опираясь на старые функции, должно быть переписано.

Мой вопрос: есть ли решение для этого, которое не полагается на устаревшие функции?

Ответ 1

Ссылка null представляет собой конструкцию ошибки в FreeMarker. Определение настраиваемого значения NULL, которое является строкой, не является хорошей идеей по причинам, которые вы упомянули. Вместо этого следует использовать следующие конструкции:

  • Параметры макроса и функции могут иметь значение по умолчанию, поэтому вызывающие могут их пропускать.
  • Чтобы проверить, есть ли переменная null, вы должны использовать оператор ??: <#if (name??)>
  • Когда вы используете переменную, которая может быть null, вы должны использовать оператор ! для указания значения по умолчанию: name!"No name"
  • Чтобы проверить, является ли последовательность (или строка) пустым, используйте ?has_content builtin: <#if (names?has_content)>

Вы можете указать пустую последовательность как значение параметра по умолчанию в макросе и просто проверить, не пусто ли она.

Ответ 2

Вот что я сделал, что, кажется, работает в большинстве сценариев:

Значение по умолчанию должно быть пустой строкой, а нуль-проверка должна быть ? has_content.

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

Ответ 3

Сотрудник предположил, что функция проверки нуля может быть самым элегантным решением:

<#assign null="NUL" />

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

Запуская эту идею, я нашел еще одно возможное решение в списке рассылки , который заключается в создании функции проверки нулей в Java путем расширения TemplateMethodModelEx. Затем я могу вставить его прямо в мою модельную карту и сделать ее доступной по всему миру во всех моих шаблонах. Полученный код Freemarker довольно чистый:

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

Кажется, это лучший способ моделирования нулевых параметров внутри макроса.

Ответ 4

Вы можете проверить, не первый ли он скаляр:

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

Ответ 5

Это еще одно решение, которое может соответствовать вашим потребностям.

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

Я определяю пустой объект по умолчанию {} вместо нуля, чтобы избежать 2 больших if else блока в моем элементе.

убедитесь, что когда вы даете значение по умолчанию для атрибута (firstName в моем случае), чтобы поместить скобку ко всему выражению, тогда! знак.

Таким образом, любой identity undefined или firstName равен undefined, freemarker использует значение по умолчанию

(user.identity.firstName)!''