Содержание
Основы HTTL¶
HTTL – это язык шаблонов, подобный Apache Velocity, который компилируется в быстрый, исполняемый байт код. В ηCMS используется модифицированная версия httl движка https://github.com/Softmotions/httl
Пример HTTL кода:
#if(books)
#for(Book book: books)
<td>${book.title}</td>
#end
#end
По умолчанию в HTTL включены 6 директив: #set, #if, #else, #for, #break, #macro.
Наличие базовых знаний языка java существенно облегчит понимание синтаксиса и конструкций HTTL.
Вывод результата выражений HTTL¶
Формат:
${expression}
Пример:
${user.name}
В данном случае результат выражений преобразуется в html код страницы так, чтобы он не был валидной html разметкой. Например, <b>text</b>, как результат выражения, преобразуется в <b>text</b> . Для того, чтобы отключить это преобразование, используется конструкция $!:
$!{expression}
В этом случае разработчик шаблона должен быть уверен, что результат выражения $!{expression} не вызовет проблем с безопасностью при отображении страницы.
Если результат выражения равен null, то HTTL выведет пустую строку:
#set(String a = null)
"${a}" == ""
Выведет: «» == «»
Комментарии в HTTL¶
Строчные комментарии помечаюся, как ## в начале строки:
## Это комментарий
Блочные коментарии начинаются с #* и заканчиваются *#:
#*
Это
блочный комментарий в HTTL
*#
Escape директивы¶
Escape директива #[…]#¶
Формат:
#[этот блок не HTTL]#
Пример:
#[This is no parse block: #if ${name}]#
Escape $ и # символов¶
Формат:
\#
\$
\\
Символ \
перед #
, $
, \
выводит эти символы как есть,
исключая их из HTTL разметки.
Выражения¶
Примечание
Выражения httl основаны на выражениях языка Java, поэтому ниже будут перечислены только отличия от правил выражений в Java.
Если в цепочке вызовов
${foo.bar.blabla}
один из элементов вернет null, то все выражение будет интерпретировано как null, а при выводе преобразовано в пустую строку.Оператор == соответствует сравнению java объектов с помощью .equals. Иными словами, foo == bar эквивалентно foo.equals(bar) в java.
Выражение в одинарных „ или двойных кавычках « интерпретируется как строка. Если есть необходимость использовать одиночный символ (типа char) то мы его заключаем в обратные кавычки ``.
- + в выражениях, где первый аргумент - число, будет интерпретироваться как
арифметическое сложение. Например: ${1 + «2»} выведет 3 вместо 12. Для конканценации строк используйте пару: ${s1}${s2}.
Доступ к значениям свойств экземпляров java классов осуществляется по имени свойств Например, ${user.name} эквивалентно вызову ${user.getName()}.
Результат выражения с логическим OR является последним ненулевым/непустым элементом выражения. Например, результатом выражения ${list1 || list2} будет list1, если list1 не пуст, в противном случае результатом будет list2.
Числовые long литералы могут быть заданы как <number>L или <number>l. Например, 3L или 3l. В случае, если используется L, результатом будет объект класса java.lang.Long, а для маленького l результатом будет примитивный long.
Для доступа к данным в списках java.util.List или в ассоциированных коллекциях java.util.Map можно использовать оператор квадратные скобки []. Например, выражение ${mylist[0]} эквивалентно ${mylist.get(0)}, а ${mymap[„foo“]} эквивалентно ${mymap.get(«foo»)}.
Результатом выражения ${[«a», «b», «c»]} является java.util.List содержащий эти элементы:
#for(color: ["red","yellow","blue"]) ${color} #end
Результатом выражения: ${[«foo»:»bar», «foo2»:»bar2»]} является java.util.Map с отношениями foo => bar и foo2 => bar2:
#for(entry: ["red":"# FF0000","yellow":"# 00FF00"]) ${entry.key} = ${entry.value} #end
Прямое обращение к статическим методам при помощи префикса @:
${@java.lang.Math.min(1,2)} ${@Math.min(1,2)}
Дополнительно отметим поддержку instanceof и new операторов:
${user instanceof httl.test.model.User}
${user instanceof User}
${new httl.test.model.User("a","b","c").name}
${new User("a","b","c").name}
Вы можете использовать оператор приведения типов () в выражениях:
<img src="$!{((Image) asm('imageA')).link}"></img>
Это приведение результата вызова метода asm к экземпляру класса Image и вызов у него java метода .getLink()
Установка переменных #set¶
Формат:
#set(type name)
#set(name = expression)
#set(type name = expression)
Где name - это имя переменной, а type - java тип переменной
Пример:
#set(firstName = "John")
#set(String lastName = "Doe")
В этом примере переменная с именем firstName должны быть определена выше по шаблону:
#set(String firstName)
Условные выражения #if и #else¶
Формат:
#if(expression)
...
#end
Пример:
#if(user.role == "admin")
...
#else(user.role =="member")
... в противном случае если роль равна 'member'
#else
... если ни то и ни другое, тогда выполняется этот блок
#end
Каждый #if должен завершаться #end после набора опциональных #else директив.
Обработка условного выражения¶
- Для не-Boolean результата эквивалентом истины(true) является:
- число, отличное от нуля
- непустая строка
- непустая коллекция
- объект, который не null
- #if(expression) эквивалентно #if(expression != null && expression != false && expression != «»)
- #if(object) эквивалентно #if(object != null)
- #if(string) эквивалентно #if(string != null && string != «»)
- #if(collection) эквивалентно #if(collection != null && collection.size > 0)
Итерация по коллекциям #for¶
Формат:
#for(name: expression)
#for(type name: expression)
Пример:
#for(books: books)
${for.index}
${for.size}
${for.first}
${for.last}
#end
В теле блока for определен объект for со следующими свойствами:
- for.index - текущий номер итерации, начиная с
0
- for.size - размер коллекции, по которой происходит итерация
- for.fist - первый элемент коллекции
- for.last - последний элемент коллекции
Явное определение типа элемента коллекции:
#for(Book book: booklist)
${book.title}
#end
В данном примере явно определяем тип элемента коллекции, к которому приводится каждый элемент.
Выполнить девять раз:
#for(9)
Вывести от одного до девяти:
#for(i: 1..9)
Вывести 10, 20, 30
, где аргумент определен, как массив []:
#for(i: [10, 20, 30])
Взять для итерации первое непустое множество books1 или books2:
#for(book: books1 || books2)
Итерации по сумме двух множеств:
#for(book: books1 + books2)
Сортировать коллекцию, затем произвести по ней итерацию:
#for(book: books.sort)
Рекурсивная итерация, элементы меню имеют метод getChildren, которые возвращают коллекцию подэлементов. Итерация по всем элементам в данной иерархии:
#for(Menu menu: menus.recursive("getChildren"))
Прерывание цикла с помощью #break¶
Формат:
#break
#break (expression)
В случае, если expression возвращает true или непустую строку, выполнение цикла будет прервано.
Примечание
Делайте условный #break прямо в теле директивы:
#break (i == j) ## правильно
Это существенно лаконичней и более производительно, чем:
#if (i == j) #break #end
Выполнение действия, если коллекция пуста #for #else¶
Формат:
#else
#else(expression)
Пример:
#for(book: books)
...
#else
... # выполняется когда коллекция пуста
#end
Библиотеки функций в контексте HTTL шаблонов¶
Регистрации библиотеки методов и доступные методы¶
В контексте HTTL шаблонов доступны библиотеки переиспользуемых методов. Библиотека переиспользуемых методов это java класс с публичными статическими методами. Библиотека может быть зарегистрирована с помощью параметра конфигурации HTTL import.methods.
Пример регистрации новой библиотеки методов в HTTL:
import.methods+=com.mycompany.MyHttlMethods
После регистрации библиотеки все публичные статические методы класса библиотеки становятся доступными в контексте HTTL шаблона и их можно переиспользовать.
По умолчанию в HTTL определены следующие библиотеки:
import.methods=httl.spi.methods.LangMethod,\
java.lang.Math,\
httl.spi.methods.SystemMethod,\
httl.spi.methods.StringMethod,\
httl.spi.methods.MathMethod,\
httl.spi.methods.TypeMethod,\
httl.spi.methods.CollectionMethod,\
httl.spi.methods.CodecMethod,\
httl.spi.methods.EscapeMethod,\
httl.spi.methods.FileMethod,\
httl.spi.methods.MessageMethod
Вы можете открыть код этих классов в проекте HTTL и изучить, какой функционал доступен в HTTL шаблонах.
Вызов библиотечных методов HTTL¶
Формат вызова метода:
${name(arg1, arg2, ...)}
${name()}
${arg1.name}
${arg1.name()}
${arg1.name(arg2, ...)}
Где name - название метода, а arg1, arg2, … - возможные аргументы метода.
Предположим, мы зарегистрировали библиотеку MyHttlMethods, как было описано выше. В нашей библиотеке - один простой метод, который добавляет Hello к переданной в качестве аргумента строке:
package com.mycompany;
public class MyHttlMethods {
public static String hello(String name) {
return "Hello " + name + "!";
}
}
Из контекста HTTL этот метод может быть вызван следующими эквивалентными способами:
${hello(«Andy»)}
${«Andy».hello}
${„Andy“.hello}
#set(String name = "Andy") ${hello(name)} ${name.hello}
Каждый из которых выведет:
Hello Andy!
Как можно видеть, первый аргумент метода может быть как аргументом явного вызова метода ${hello(name)}, так и контекстом для вызова этого метода без первого аргумента: ${name.hello}.
Давайте добавим в нашу библиотеку еще один метод, который немного расширяет функционал первого и позволяет к строке приветствия добавить произвольное сообщение:
package com.mycompany;
public class MyHttlMethods {
public static String hello(String name) {
return "Hello " + name + "!";
}
public static String hello(String name, String msg) {
return hello(name) + " " + msg;
}
}
Тогда, в дополнение к существующим возможностям, мы сможем вывести Hello Andy! Great to see u! любым из ниже перечисленных способов:
${hello("Andy", "Great to see u!")}
${"Andy".hello("Great to see u!")}
Пример использования метода :js:func:`toCycle` из `httl.spi.methods.CollectionMethod`
Вывод списка продуктов с циклически меняющимся цветами строк из набора colors:
#set(colors = ["red","blue","green"].toCycle)
<table>
#for(item: list)
<tr style="color:${colors.next}">
<td>${item.name}</td>
</tr>
#end
</table>
Макросы #macro¶
Макрос - это блок HTTL разметки, который можно переиспользовать. Макрос может принимать набор параметров, аналогично параметрам функции в java. При вызове макроса HTTL разметка, определенная в макросе, вставляется в место вызова макроса.
Формат определения макроса:
#macro(name)
#macro(name(arg1, arg2, ...))
#macro(name(type1 arg1, type1 arg2, ...))
Где name - имя макроса, arg1, arg2, … - возможные аргументы макроса, type1, type2, … - опциональные типы аргументов макроса.
Формат вызова макроса:
${name(arg1, arg2)}
Где name - имя макроса, arg1, arg2, … - возможные аргументы макроса.
Макросы могут применяться в случае наследования HTTL шаблонов.
Включение в разметку других файлов¶
Семейство include методов из httl.spi.methods.FileMethod позволяют включать другие файлы разметки в текущую разметку.
Пример: включение контента template.httl в разметку:
${include("/template.httl")}
Передача дополнительных аргументов при включении:
${include("/template.httl", ["arg":"value"])}
Использование относительного пути до файла:
${include("../template.httl")}
Примечание
Файл, включаемый при помощи метода include, интерпретируется как HTTL разметка.
Включение содержимого файла в текущее место разметки:
${read("/text.txt")}
Примечание
Файл, включаемый при помощи метода read, не интерпретируется как HTTL разметка.