golang

Шаблонизатор GO text template

  • воскресенье, 11 февраля 2024 г. в 00:00:13
https://habr.com/ru/articles/792802/

Введение

Шаблоны - это инструмент программирования, который позволяет создавать структурированный и повторно используемый контент или код. Они имеют много применений в различных областях, включая веб-разработку, генерацию отчетов, форматирование текста и многое другое. В целом, шаблоны предоставляют удобный способ создания структурированного и гибкого контента или кода, что упрощает создание контента для различных приложений и веб-сайтов.

Все инструкции шаблона заключаются в символы

{{ и }}

Текст вне этих символов является простым статичным текстом. Статичный текст копируется из шаблона в конечный вывод без какого-либо изменения.

В шаблонизаторах, таких как Go или HTML-шаблоны, символ . (точка) обычно используется для обращения к текущему контексту данных. Когда вы используете . в шаблоне, вы обращаетесь к данным, которые были переданы в этот шаблон. Допустим вы передали в шаблон вашего кота, теперь ваш кот в этом шаблоне является точкой, а узнать его кличку вы можете так:

.Name

Если данные имеют вложенную структуру это не беда, допустим, вы хотите узнать калорийность яблока из вашей структуры фруктов, тогда можно создать такую схему: .Apple.Calories;

В шаблоне можно оставлять комментарии

{{/* комментарий */}}

Условный оператор if - else

Ниже приведён пример в котором мы в рамках шаблона будем рассуждать чем нам нужно заниматься в зависимости от времени суток. Представим, что переменная Night хранит в себе true если сейчас ночь. Итак:

{{ if .Night }} Надо спать {{ else }} Надо работать {{ end }}

Если значение переданной переменной будет равно 0, "", nil, или пустым массивом, срезом, это будет воспринято как false (условие не выполнится).

Допустим также вариант else if - он выполняется если верно второе условие, допустим:

{{if .Night }} Надо спать {{else if .Morning}} Надо покормить кота {{end}}

Цикл range

{{ range .Items }} Привет! {{ end }}

Если в массиве .Items 5 элементов то шаблон поздоровается 5 раз. 

Цикл вызывает по очереди каждый элемент массива. Чтобы вывести текущий элемент массива .Items внутри цикла range, вы можете использовать встроенную переменную. Если у нас есть массив мальчишек [Лёша, Петя, Саша], то можно поздороваться со всеми сразу. Вот как это можно сделать:

{{ range .Items }}
   Привет, {{ . }}!
{{ end }}

Обратите внимание, что через символ точки мы обращаемся к элементу массива текущей итерации.

Результат: Привет, Леша! Привет, Петя! Привет, Саша!

Также возможна вариация с else, который выполнится если массив пуст:

{{range .Items}} Привет, {{ . }}! {{else}} Никому не привет! {{end}}

Если вы хотите прервать цикл то можете использовать следующий оператор внутри цикла:

{{break}}

Если хотите пропустить какого то человека и не здороваться с ним можете применить эту конструкцию:

{{continue}}

Пример: Если вы не хотите здороваться с Лёшей можете применить следующее решение:

{{ range .Items }}
   {{ if eq . "Лёша" }} /* проверка соответствия базовой функцией eq*/
      {{ continue }}
   {{ end }}
Привет, {{ . }}!
{{ end }}

Результат: Привет, Петя! Привет, Саша!  

Шаблоны

Можно собирать шаблоны из других шаблонов! Прям как конструктор лего, для этого нам надо дать шаблонам имя, затем их можно будет вызывать по имени из других шаблонов. В следующем примере мы построим дом (а домашнее задание - посадить сына и вырастить дерево) Зададим шаблоны:

{{define "Top"}} Крыша {{end}}
{{define "Bottom"}} Фундамент {{end}}

У нас есть два шаблона - шаблон содержащий крышу и фундамент, попробуем собрать из них дом, вызовем их с помощью слова template:

{{template "Top"}} 
Стены
{{template "Bottom"}}

Результат:

Крыша

Стены

Фундамент

В вызываемый шаблон можно передавать переменные. Допустим у нас есть шаблон для приветствия дорогих коллег:

{{ define "name" }}
    Привет, {{ . }}!
{{ end }}

Допустим у нас есть переменная с именем нашего друга .Name = "Максимка", Мы можем поздороваться с ним с помощью шаблона следующим образом:

{{template "name" .Name}}

Результат: Привет, Максимка!

Следующая конструкция передаст в вызываемый шаблон все переменные из текущего шаблона, в следствие чего к ним можно будет обращаться по их имении. Допустим у нас есть переменные .Name  и .Age, содержащие в себе имя и возраст, тогда:

{{ define "name" }}
    Привет, {{ .Name }}! Тебе уже {{ .Age }} лет.
{{ end }}
/* Вызов шаблона */
{{template "name" .}}

With

Конструкция with в шаблонах используется для изменения контекста данных внутри блока шаблона. Она позволяет временно изменить текущий контекст на другой объект или значение. Вот как это работает:

{{with .Data}}
T1
{{end}}

Если значение Data не пустое (не nil или не пустой массив, слайс, карта и т. д.), то T1 будет выполнено в контексте этого значения. Внутри блока T1 имеет доступ к данным в Data. Если pipeline пустое, то никакого вывода не будет сделано, и T1 не будет выполнено. Теперь, если мы хотим предоставить альтернативный блок для выполнения в случае, если pipeline пустое, мы можем использовать следующую конструкцию:

{{with .Data}}
T1
{{else}}
T0
{{end}}

Здесь, если значение Data не пустое, то выполнится T1. Если же Data пустое, то будет выполнено T0.

Таким образом, with предоставляет удобный способ изменить контекст данных внутри блока шаблона, что может быть полезно при работе с вложенными структурами или для предоставления альтернативного поведения в зависимости от наличия данных.  

Стандартные и пользовательские функции

В шаблонизаторе GO два типа функций — встроенные и пользовательские.

Вызов функции любого типа выглядит так:

funcname arg1 arg2 arg3

Список стандартных функций:

call funcLocation arg1 arg2 — встроенная функция call позволяет вызывать другие функции, например функции, хранящиеся в переменных или чье имя находится в переменной;

  • index x 1 2 3 — получение элемента среза/массива/мапы;

  • slice x 1 2 — получение нового среза из среза, аналогично

  • s[1:2]; len x — получение длины среза/массива/мапы;

  • print, printf, println — явный вывод данных;

Булевы операторы в шаблонах также реализованы как функции:

  • eq arg1 arg2 — arg1 == arg2

  • ne arg1 arg2 — arg1 != arg2

  • lt arg1 arg2 — arg1 < arg2 

  • le arg1 arg2 — arg1 <= arg2

  • gt arg1 arg2 — arg1 > arg2

  • ge arg1 arg2 — arg1 >= arg2

Разработчики могут создавать собственные функции если это необходимо для проекта.

Ссылки