Сайт на 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
	}

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

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

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

3 Comments

  1. Баг. На данной странице не отображается картинка со структурой проекта

    1. Привет! Спасибо за комментарий. Да, картинка куда-то пропала. Попробую восстановить.

    2. Статья писалась почти пять лет назад и за это время WP много раз обновлялся. К сожалению, исходную картинку восстановить не удалось. Но можно посмотреть скриншот структуры проекта из следующего урока и убрать оттуда все, что мы добавляем там. Должно получиться что-то вроде этого:
      https://alextonkonogov.ru/wp-content/uploads/2024/07/image-5.png

      Будет время, я воспроизведу проект и выложу его на github в отдельных ветках для каждого урока.

Leave a Comment

Your email address will not be published. Required fields are marked *