Сайт на Golang. Часть 2. Работаем с HTML

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

Статические страницы

Если мы хотим, чтобы при при заходе на стартовую страницу пользователю вместо текстового сообщения открывалась страничка с HTML контентом , то нам достаточно сделать следующее:

В папке «public» создаем новую папку «html». Затем создаем и размещаем там готовую страницу «startStaticPage.html» .

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>Приветствую тебя на стартовой странице этого сайта!</h1>
    <ul>
        <li>Первый элемент маркированного списка</li>
        <li>Второй элемент маркированного списка</li>
        <li>Третий элемент маркированного списка</li>
    </ul>
</body>
</html>

Переписываем хендлер главной страницы:

package controller

import (
	"github.com/julienschmidt/httprouter"
	"html/template"
	"net/http"
	"path/filepath"
)

func StartPage(rw http.ResponseWriter, r *http.Request, p httprouter.Params) {
	//указываем путь к нужному файлу
	path := filepath.Join("public", "html", "startStaticPage.html")

	//создаем html-шаблон
	tmpl, err := template.ParseFiles(path)
	if err != nil {
		http.Error(rw, err.Error(), 400)
		return
	}

	//выводим шаблон клиенту в браузер
	err = tmpl.Execute(rw, nil)
	if err != nil {
		http.Error(rw, err.Error(), 400)
		return
	}
}

Проверяем результат:

При этом необязательно использовать HTML файлы (хотя предпочтительнее и удобнее). Мы можем «сверстать» наш контент прямо в коде в виде обычной строки, а затем превратить его в HTML и отправить клиенту:

func StartPage(rw http.ResponseWriter, r *http.Request, p httprouter.Params) {
    //верстаем контент страницы в виде обычной строки
    content :=  `<html lang="ru">
	                     <head>
		                       <meta charset="UTF-8">
		                       <title>Title</title>
		                   </head>
		                   <body>
		                       <h1>Приветствую тебя на стартовой странице этого сайта!</h1>
		                       <ul>
		                           <li>Первый элемент маркированного списка</li>
			                         <li>Второй элемент маркированного списка</li>
			                         <li>Третий элемент маркированного списка</li>
			                         <li>Четвертый элемент маркированного списка</li>
		                       </ul>
		                   </body>
		                   </html>`
        //создаем html-шаблон
	tmpl, err := template.New("example").Parse(content)
	if err != nil {
		http.Error(rw, err.Error(), 400)
		return
	}
	tmpl.Execute(rw, content)
}

Результат:

Динамические страницы

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

Создадим страницу «usersDynamicPage.html» в папке «html»:

{{define "users"}}

<!doctype html>
<html lang="ru" class="h-100">
<head>
    <title>Title</title>
</head>
<body class="d-flex flex-column h-100">
    <table class="table">
        <th>id</th>
        <th>Имя</th>
        <th>Фамилия</th>

        {{range .}}
        <tr>
            <td>{{.Id}}</td>
            <td>{{.Name}}</td>
            <td>{{.Surname}}</td>
        </tr>
        {{end}}

    </table>
</body>
</html>

{{end}}

Обратите внимание, что здесь используются две специальные конструкции:

  • {{define «users»}}{{end}} — объявляется начало и конец именованного шаблона «users»;
  • {{range .}} {{end}} — объявляется начало и конец цикла, при помощи которого будут генерироваться строки таблицы на основании массива переданных данных. Переданные данные у нас не имеют ключа, поэтому представлены в виде точки. Однако внутри каждого из элементов массива есть элементы структуры «Id», «Name» и «Surname», обращение к которым возможно только по ключу.

Теперь перепишем наш хендлер для вывода списка пользователей:

package controller

import (
	"github.com/julienschmidt/httprouter"
	"goSiteProject/app/model"
	"html/template"
	"net/http"
	"path/filepath"
)

func GetUsers(rw http.ResponseWriter, r *http.Request, p httprouter.Params) {
	//получаем список всех пользователей
	users, err := model.GetAllUsers()
	if err != nil {
		http.Error(rw, err.Error(), 400)
		return
	}

	//указываем путь к файлу с шаблоном
	main := filepath.Join("public", "html", "usersDynamicPage.html")

	//создаем html-шаблон
	tmpl, err := template.ParseFiles(main)
	if err != nil {
		http.Error(rw, err.Error(), 400)
		return
	}

	//исполняем именованный шаблон "users", передавая туда массив со списком пользователей
	err = tmpl.ExecuteTemplate(rw, "users", users)
	if err != nil {
		http.Error(rw, err.Error(), 400)
		return
	}
}

Проверяем результат:

К слову, можно использовать условия в виде конструкций {{if}}{{else}}{{end}}, которые можно применять при генерации страниц. С ними вы при желании сможете ознакомиться отдельно, но для примера мы попробуем выводить пользователей, начиная с пятого.

        {{range .}}
        <tr>
            {{if gt .Id 4}}
            <td>{{.Id}}</td>
            <td>{{.Name}}</td>
            <td>{{.Surname}}</td>
            {{end}}
        </tr>
        {{end}}

Результат:

Теперь попробуем собрать одну страницу из нескольких частей.

В папке «public/html» создаем новый файл «common.html» и размещаем там следующий код:

{{define "meta"}}
    <meta charset="utf-8">
{{end}}

{{define "styles"}}
    <style>
        table th {color: red};
    </style>
{{end}}


{{define "scripts"}}
    <script>
        document.addEventListener('DOMContentLoaded', function(){
            setTimeout(sayHello, 1000);
        });

        function sayHello() {
            alert("Hello from scripts!");
        }
    </script>
{{end}}

Далее модифицируем файл «usersDynamicPage.html». Внутри тега head размещаем ссылки на именованные шаблоны со стилями и метаданными:

<head>
    {{template "meta"}}
    {{template "styles"}}
    <title>Title</title>
</head>

Перед закрывающим тегом body размещаем ссылку на именованный шаблон со скриптами:

{{template "scripts"}}
</body>

Теперь доработаем немного хендлер вывода страницы со списком сотрудников, так, чтобы при генерации мы могли использовать именованные шаблоны и из файла «common.html».

	//указываем пути к файлам с шаблонами
	main := filepath.Join("public", "html", "usersDynamicPage.html")
	common := filepath.Join("public", "html", "common.html")

	//создаем html-шаблон
	tmpl, err := template.ParseFiles(main, common)
	if err != nil {
		http.Error(rw, err.Error(), 400)
		return
	}

Проверяем результат:

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

Структура нашего проекта на текущий момент: