Привет, Хабр!

В этой статье хочу поговорить про, пожалуй, мою самую любимую библиотеку для Go. Это Jet templates. Библиотека, которая очень упрощает работу с шаблонами(templates) в Go.

Работать со встроенными шаблонами в Го я очень не люблю. Прописывание мап, неудобная работа в самих шаблонах. Jet это решает.

Передача данных

Для того, чтобы сделать мапу с данными для шаблона, необходимо написать всего одну строку:

data := make(jet.Varmap)

А для того, чтобы что-то туда записать, необходимо написать так:

data.Set("data", any)

Теперь о том, как это использовать:

Рендер шаблона

views := jet.NewHTMLSet(pathToTemplates) // Получаем абсолютно все шаблоны по 
										 // этому пути

Чтобы отрендерить определенный шаблон, необходимо написать это:

t, err := tc.GetTemplate(templateName) // Получаем определенный шаблон

// обработка ошибки

err = t.Execute(w, data, nil) // где w - ResponseWriter, data - jet.Varmap с данными.

Тут важно, что данные необходимо поставить на второе место в аргументах, а не на третье, хотя название третьего аргумента и называется "data", надо ставить на "variables"

Но, по надобности, можете написать и собственную структуру с данными и тогда уже ставьте ее на третье место:

data := struct {
		Data string
	}{
		Data: "hello",
	}
err = t.Execute(w, nil, data)

Вот самый простой пример использования jet:

package main

import (
	"log"
	"net/http"

	"github.com/CloudyKit/jet"
)

func main() {
	http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
		set := jet.NewHTMLSet("./templates")

		data := make(jet.VarMap)

		t, err := set.GetTemplate("hello.html")
		if err != nil {
			log.Fatal(err)
		}

		data.Set("message", "Hello World!")

		t.Execute(w, data, nil)
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}

Обработка должна происходить в http-хэндлере, но ее можно вынести в отдельный пакет “render” и передавать туда необходимые данные по типу "w ResponseWriter, data jet.Varmap, templateName string" и уже это вызывать из хэндлера.

Я использую встроенный в стандартный пакет net/http “ResponseWriter”, но как это делается в том же GoFiber - не знаю, думаю в его fiber.Ctx это быть должно

Синтаксис шаблонов

Много, о чем можно рассказать, поэтому если понадобится, то читайте тут: https://github.com/CloudyKit/jet/wiki/3.-Jet-template-syntax

Начну с использования данных, которые были переданы

Принятие данных

Пусть у нас будет data с message равным “Hello World!”

data.Set("message", "Hello World!")

Теперь в шаблоне в двойных фигурных скобках пишем название переменной, которую записали в data:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title></title>
    <link href="css/style.css" rel="stylesheet" />
  </head>
  <body>
    <div>{{message}}</div>
  </body>
</html>

Наша страница:

Страница в браузере
Страница в браузере

Но, представим, Вам захотелось использовать собственно написанную структуру:

data := struct {
		Data string
	}{
		Data: "hello",
	}
err = t.Execute(w, nil, data)

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

<div>{{.Data}</div>

Циклы

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

{{range keyOrIndex, value := array}}
Ваши действия
{{end}}

Но, можно обойтись и без индекса:

{{range value := array}}
Ваши действия
{{end}}

Скажу даже больше, можно убрать все и оставить только массив:

{{range array}}
<p>Выводим текущий элемент: {{.}}</p>
{{end}}

Layouts

Еще можно легко работать с layouts. Для этого надо создать файл с содержимым лэйаута, например такой:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title></title>
    <link href="css/style.css" rel="stylesheet" />
  </head>
  <body>
    {{yield body()}}
  </body>
</html>

“yield” вызывает кусок кода из другого файла. Давайте теперь создадим еще один файл с содержимым body():

{{extends "путь к вашему layout"}} 
{{block body()}}
<div>Что-то<div>
{{end}}

Таких файлов с содержимым body может быть неограниченное кол-во. Вы можете создавать и другие блоки и распологать их в другом месте, например не в body, а в title с одноименным названием

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

Заключение

Надеюсь, Вы открыли для себя эту замечательную библиотеку и Вам станет легче писать шаблоны в Go. Это и еще многое читайте в офиц. репозитории Jet templates: https://github.com/CloudyKit/jet/

Комментарии (9)


  1. binque
    21.08.2024 05:34
    +1

    Пользуюсь pongo2. Синтаксис и возможности соответствуют Django с некоторыми небольшими исключениями. Описанные в статье функции тоже поддерживаются. Пользоваться удобно. За годы использования написал для себя еще несколько расширений.


    1. mo0Oonnn Автор
      21.08.2024 05:34

      интересно. А почему именно с Django-синтаксисом?


      1. binque
        21.08.2024 05:34

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


  1. NeoCode
    21.08.2024 05:34
    +1

    Для своих домашних поделок пользуюсь встроенным в Go шаблонизатором. Практически все устраивает. Из того что не устраивает - нет встроенных арифметических и логических операций. Т.е. нельзя написать например {{.MyVariable+1}} . Да, там есть возможность подключить внешние пользовательские функции и их вызывать, но уж слишком много возни и синтаксис неочевидный. В итоге оказывается проще посчитать всё что нужно в коде на Go и передать в дополнительных полях структуры.


  1. ghostiam
    21.08.2024 05:34
    +1

    Сейчас набирает популярность Templ, позволяет писать шаблон почти как код на go и преобразуется в go код после генерации(с нормальными типами, а не просто map), то есть никакого парсинга шаблонов в рантайме.
    Мне понравился, но поддержка редакторов пока скудная.


    1. mo0Oonnn Автор
      21.08.2024 05:34

      Надо будет почитать. Интересно, не видел ещё такого.


  1. SwiftBike
    21.08.2024 05:34

    А по скорости как? Пока ничего лучше bytes.Replace() не нашел


    1. mo0Oonnn Автор
      21.08.2024 05:34

      А как шаблоны связаны с bytes.Replace ?


      1. SwiftBike
        21.08.2024 05:34

        Идея же в том чтобы поменять {{message}} на хелоу ворлд? Или кусок html шаблона на другой? Редактировать темплейты работая напрямую со стрингой или байтами через реплейсы выходит быстрее.