Подробное руководство по define, template и block в html/template

В пакете html/template есть три основные директивы для работы с повторно используемыми частями шаблонов: define, template, block

Основные концепции

В пакете html/template есть три основные директивы для работы с повторно используемыми частями шаблонов:

  1. {{define "name"}}...{{end}} - определяет именованный шаблон
  2. {{template "name"}} - вставляет именованный шаблон
  3. {{block "name"}}...{{end}} - комбинация define+template с возможностью переопределения

1. Директива define

Где описывать:

  • В любом месте шаблона (но обычно в начале или конце файла)
  • В отдельных файлах, которые потом объединяются

Пример определения:

{{define "header"}}
<header>
    <h1>{{.Title}}</h1>
    <nav>
        <a href="/">Home</a>
        <a href="/about">About</a>
    </nav>
</header>
{{end}}

{{define "footer"}}
<footer>
    <p>Copyright © {{.Year}} My Company</p>
</footer>
{{end}}

2. Директива template

Где применять:

  • В любом месте основного шаблона
  • Можно передавать данные (по умолчанию - текущий контекст)

Пример использования:

<!DOCTYPE html>
<html>
<head>
    <title>{{.Title}}</title>
</head>
<body>
    {{template "header" .}}  <!-- Передаем текущий контекст -->
    
    <main>
        {{.Content}}
    </main>
    
    {{template "footer" .}}  <!-- Передаем текущий контекст -->
</body>
</html>

Передача другого контекста:

{{template "user_profile" .UserData}}

3. Директива block

Особенности:

  • Комбинация define + template
  • Можно переопределять в дочерних шаблонах
  • Если не переопределен - используется содержимое по умолчанию

Пример:

<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
    {{block "head" .}}
    <title>{{.Title}} - Default</title>
    {{end}}
</head>
<body>
    {{block "content" .}}
    <p>Default content</p>
    {{end}}
</body>
</html>

Переопределение в дочернем шаблоне:

<!-- home.html -->
{{define "content"}}
<h1>Welcome!</h1>
<p>This is the home page.</p>
{{end}}

{{template "base.html" .}}

Практические схемы организации

1. Многофайловая структура

$$templates/ ├── base.html # Основной каркас ├── header.html # Шапка сайта ├── footer.html # Подвал ├── home.html # Главная страница └── about.html # Страница "О нас"$$

base.html:

<!DOCTYPE html>
<html>
<head>
    {{template "head" .}}
</head>
<body>
    {{template "header" .}}
    {{block "content" .}}{{end}}
    {{template "footer" .}}
</body>
</html>

home.html:

{{define "content"}}
<h1>Welcome</h1>
<p>Home page content</p>
{{end}}

{{template "base.html" .}}

2. Наследование шаблонов

// Загрузка шаблонов
templates := template.Must(template.ParseFiles(
    "base.html",
    "header.html",
    "footer.html",
    "home.html",
))

// Исполнение конкретного шаблона
err := templates.ExecuteTemplate(w, "home.html", data)

3. Вложенные блоки

<!-- base.html -->
{{define "layout"}}
<div class="container">
    {{block "sidebar" .}}
    <!-- Default sidebar -->
    <div class="sidebar">Default sidebar</div>
    {{end}}
    
    <div class="content">
        {{template "content" .}}
    </div>
</div>
{{end}}
<!-- profile.html -->
{{define "sidebar"}}
<div class="profile-sidebar">
    <img src="{{.User.Avatar}}">
    <h3>{{.User.Name}}</h3>
</div>
{{end}}

{{define "content"}}
<div class="profile-content">
    <!-- Контент профиля -->
</div>
{{end}}

{{template "layout" .}}

Особенности работы

  1. Порядок загрузки: шаблоны, на которые есть ссылки, должны быть загружены до их использования
  2. Контекст данных: по умолчанию передается текущий контекст, но можно передать другой
  3. Рекурсия: шаблоны могут вызывать сами себя (но осторожно с бесконечной рекурсией)
  4. Переопределение: последний загруженный шаблон с тем же именем переопределяет предыдущий

Пример с пользовательскими функциями

func main() {
    funcMap := template.FuncMap{
        "formatDate": func(t time.Time) string {
            return t.Format("2006-01-02")
        },
    }

    tmpl := template.Must(
        template.New("base").Funcs(funcMap).ParseFiles("base.html", "home.html"),
    )

    err := tmpl.ExecuteTemplate(os.Stdout, "home.html", data)
}
<!-- В шаблоне: -->
<p>Created: {{.CreatedAt | formatDate}}</p>

Лучшие практики

  1. Разделяйте шаблоны на логические компоненты
  2. Используйте block для переопределяемых секций
  3. Основной каркас выносите в base.html
  4. Называйте шаблоны осмысленно (не “template1”, а “user_profile”)
  5. Документируйте назначение шаблонов в комментариях