DevGang
Авторизоваться

Как использовать шаблоны в Golang? 

В этой статье я объясню основы стандартных пакетов шаблонов языка Go (Golang). Эти основы включают в себя использование переменных, условных операторов, перебор переменных и применение функций к переменным в шаблонах Golang.

Golang предоставляет пакеты text/template и html/template для работы с шаблонами прямо из коробки.

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

Эти пакеты содержат различные функции, которые могут загружать, анализировать и оценивать текст шаблона или файлы (HTML или текст).

Например, вы можете использовать следующие функции:

  1. Используйте Parse для анализа текстовых строк, присутствующих внутри вашей программы.
  2. Используйте ParseFiles для загрузки и анализа файлов шаблонов.
  3. Используется Execute для вывода шаблона на некоторый вывод с использованием определенных полей данных.

Далее я расскажу об основных строительных блоках для создания мощных шаблонов в Golang.

Внешние (программные) переменные

Вы можете отправлять переменные из вашей реальной программы Go в свой шаблон. Затем вы можете использовать эти переменные в шаблонах.

Прежде всего, если вы хотите отобразить определенные действия в своих шаблонах, вы можете сделать это, добавив эти действия между двойными скобками {{}} в ваших текстовых строках.

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

Например {{.}}, отобразит все ваши поля данных в виде форматированной строки.

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

Например, поле {{ .Title }} будет отображаться только как форматированная строка Title.

Вы можете увидеть применение этих действий в примере кода ниже.

package main 
import ( 
    "os" 
    "text/template" 
) 
type Book struct { 
    Title     string 
    Publisher string 
    Year      int 
} 
func main() { 
    t1 := template.New("Template") 
    t1, _ = t1.Parse("External variable has the value [{{.}}]\n") 
    t1.Execute(os.Stdout, "Amazing") 
    b := Book{"The CSound Book", "MIT Press", 2002} 
    t1.Execute(os.Stdout, b) 
    t2 := template.New("Template") 
    t2, _ = t2.Parse("External variable Book has the values [Title: {{.Title}}, Publisher: {{.Publisher}}, Year: {{.Year}}]\n") 
    t2.Execute(os.Stdout, b) 
}
// Output
// External variable has the value [Amazing] 
// External variable has the value [{The CSound Book MIT Press 2002}] 
// External variable Book has the values [Title: The CSound Book, Publisher: MIT Press, Year: 2002]

Сначала мы создаем новый пустой шаблон template.New() с приведенным выше кодом. Затем мы анализируем строку в этом шаблоне, используя Parse(). В этой строке мы добавили одно действие между двойными скобками.

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

Рендеринг шаблона с переменной выполняется с помощью Execute(). Первый параметр (os.Stdout) — это место, куда должен быть выведен визуализированный шаблон. Второй параметр (Amazing) — это переменная, которую мы хотим отобразить в шаблоне. В данном случае это просто строка.

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

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

Обратите внимание, что функция Parse на самом деле возвращает две переменные. Первая переменная — это анализируемый шаблон, а вторая — сообщение об ошибке. Мы не будем использовать сообщение об ошибке в приведенном выше примере, поэтому просто поставим подчеркивание.

Внутренние (шаблонные) переменные

Можно определить внутренние переменные, т.е. определенные только в шаблонах.

В следующем шаблоне показано использование переменной, определенной в строке шаблона. Сначала определение, а потом употребление. Внутренние переменные должны начинаться со знака доллара ($).

{{$var:=`value`}}  ... {{$var}} ...

Ниже приведен простой пример:

package main
import (
    "os"
    "text/template"
)
func main() {
    t, _ := template.New("Template").Parse("{{$var:=2150}}Internal variable has the value [{{$var}}]")
    t.Execute(os.Stdout, nil)
}
// Output:
// Internal variable has the value [2150]

В отличие от рендеринга внешней переменной с помощью точки, здесь мы упоминаем переменную, которую мы ранее определили в двойных скобках.

Условные выражения

Ниже вы можете увидеть общий шаблон для условных выражений:

{{if [..]}} if-part {{end}}
{{if [..]}} if-part {{else}} else-part {{end}}
{{if [..]}} if-part {{if else}} if-else-part {{end}}

В последнем шаблоне вы можете использовать часть {{if else}} столько раз, сколько необходимо, а также можете добавить блок {{else}}. Ниже приведен простой пример второго шаблона:

package main 
import ( 
    "os" 
    "text/template" 
) 
func main() { 
    t, err := template.New("Template").Parse("{{if eq . `filler`}}This is filler...{{else}}It's something else...{{end}}\n") 
    if err != nil { 
        panic(err) 
    } 
    t.Execute(os.Stdout, "filler") 
}
// Output:
// This is filler...

Обратите внимание, что в приведенном выше примере мы также используем сообщение об ошибке, возвращаемое функцией Parse(). Если ошибки нет, сообщение об ошибке равно 'nil'. Мы можем обработать ошибку с помощью функции panic(), если есть ошибка.

Кроме того, вместо равенства (==) вы также можете использовать:

  1. != - не равно
  2. < - меньше
  3. <= - меньше или равно
  4. > - больше
  5. >= - больше или равно

Кроме того, вместо оператора сравнения можно написать логическую переменную.

package main
import (
    "os"
    "text/template"
)
func main() {
    t, _ := template.New("Template").Parse("{{if .}}This is true.{{else}}This is false.{{end}}\n")
    t.Execute(os.Stdout, false)
}
// Output:
// This is false

Циклы

При переборе массива, среза или карты в шаблоне можно использовать несколько шаблонов. Сначала рассмотрим самую простую форму.

{{range .Var}}
    {{.}}
{{end}}

Здесь мы перебираем каждую переменную в массиве, срезе или карте. На каждом шаге цикла одна переменная будет доступна для использования в цикле.

Теперь {{.}} больше не представляет все переменные, доступные нашему шаблону, а только ту, которая доступна для использования в цикле.

package main 
import ( 
    "os" 
    "text/template" 
) 
func main() { 
    computerList := []string{"Arduino", "Raspberri Pi", "NVidia Jetson Nano"} 
    t, err := template.New("Template").Parse("My favorite computers are:\n{{range .}}{{.}}\n{{end}}\n") 
    if err != nil { 
        panic(err) 
    } 
    t.Execute(os.Stdout, computerList) 
}
// Output:
// My favorite computers are: 
// Arduino 
// Raspberri Pi 
// NVidia Jetson Nano

Более сложный шаблон, который вы можете использовать при переборе нескольких переменных, выглядит следующим образом:

{{range $index, $element := . }} 
    {{$index}} ... {{$element}} ...
{{end}}

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

Обратите внимание, что переменные $index, и $element являются внутренними переменными, определенными в шаблоне. По этой причине они также начинаются со знака «$».

package main 
import ( 
    "os" 
    "text/template" 
) 
func main() { 
    dishesList := []string{"Enciladas con Pollo", "Hot&Spicy Pizza", "Spaghetti Bolognese"} 
    t, err := template.New("Template").Parse("My favorite dishes are:\n{{range $index, $item:=.}}{{$index}}) {{$item}}\n{{end}}\n") 
    if err != nil { 
        panic(err) 
    } 
    t.Execute(os.Stdout, dishesList) 
}
// Output
// My favorite dishes are: 
// 0) Enciladas con Pollo 
// 1) Hot&Spicy Pizza 
// 2) Spaghetti Bolognese

Как видите, у этого подхода есть одна проблема. Упорядоченный список, который мы получили на выходе, начинается с 0. Причина в том, что первый индекс всегда равен 0.

Для многих приложений это не то, что нам нужно. Нам бы хотелось, чтобы упорядоченный список начинался с 1. Так как же увеличить каждый индекс на 1? Нам понадобится функция для увеличения данной переменной.

В следующей части мы рассмотрим запуск функций в строке шаблона.

Функции внутри шаблона

Можно использовать функции внутри шаблона.

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

Мы создадим функцию сложения add(), вызываемую в приведенном ниже примере. Мы добавим его в отображение функции, используя template.FuncMap{}внутри функции FuncMap().

С помощью функции add мы сможем добавить 1 к переменной $index и, таким образом, увеличить ее. Результатом будет более читаемый упорядоченный список.

package main 
import ( 
    "os" 
    "text/template" 
) 
func add(a, b int) int { 
    return a + b 
} 
func main() { 
    dishesList := []string{"Enciladas con Pollo", "Hot&Spicy Pizza", "Spaghetti Bolognese"} 
    t, err := template.New("Template").Funcs(template.FuncMap{"add": add}).Parse("My favorite dishes are:\n{{range $index, $item:=.}}{{add $index 1}}) {{$item}}\n{{end}}\n") 
    if err != nil { 
        panic(err) 
    } 
    t.Execute(os.Stdout, dishesList) 
}
// Output:
// My favorite dishes are: 
// 1) Enciladas con Pollo 
// 2) Hot&Spicy Pizza 
// 3) Spaghetti Bolognese

Вы можете найти более сложный пример ниже. Здесь мы добавляем вторую функцию. Идея состоит в том, чтобы создать вывод в стиле CSV, но где мы можем выбрать символ разделителя, который нам нужен, когда мы добавляем «разделитель» к отображению функции.

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

package main
import (
    "os"
    "text/template"
)
func add(a, b int) int {
    return a + b
}
func delimiter(s string) func() string {
    return func() string {
        return s
    }
}
func main() {
    dishesList := []string{"Enciladas con Pollo", "Hot&Spicy Pizza", "Spaghetti Bolognese"}
    tmpl := "Index{{dl}}Dish\n{{range $index, $item:=.}}{{add $index 1}}{{dl}}{{$item}}\n{{end}}\n"
    funcMap := template.FuncMap{"add": add, "dl": delimiter(",")}
    t, _ := template.New("Template").Funcs(funcMap).Parse(tmpl)
    t.Execute(os.Stdout, dishesList)
}
// Output:
// Index,Dish
// 1,Enciladas con Pollo 
// 2,Hot&Spicy Pizza 
// 3,Spaghetti Bolognese

Существует удобная библиотека с сопоставлениями шаблонных функций, которая называется Sprig. Вы можете найти ее здесь. Однако эта библиотека работает только с пакетом html/template.

#Golang
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

Vladimir Shaitan - Видео блог о frontend разработке и не только

Посмотреть