Введение

Динамичные и интерактивные веб-приложения уже давно создаются на JavaScript.

Создадим элегантное приложение  —  список задач, которое, если отмечать пункты галочками, мгновенно обновляется без перезагрузки всей страницы. И никакого JavaScript, только Golang и htmx. Благодаря этой мощной комбинации создаются адаптивные и интерактивные веб-приложения.

Попутно реализуем базовые операции создания и удаления пользователей.

Что такое htmx?

Это современное расширение HTML для добавления двунаправленного взаимодействия между браузером и сервером. Кроме создания динамических веб-страниц без единой строчки JavaScript, им прямо в HTML обеспечивается доступ к AJAX, отправляемым сервером событиям и т. д.

Принцип работы htmx

  • При взаимодействии пользователя с элементом, в котором имеется атрибут htmx, соответствующее событие, например нажатие кнопки, активируется браузером.
  • Событие перехватывается в htmx, и на конечную точку серверной части, указанную в атрибуте, например hx-get="/my-endpoint", отправляется HTTP-запрос.
  • В конечной точке серверной части запрос обрабатывается и генерируется HTML-ответ.
  • В htmx ответ получается, и DOM обновляется в соответствии с атрибутами hx-target и hx-swap. При этом:

— Все содержимое элемента заменяется.
 —  До или после элемента вставляется новое содержимое.
 —  Содержимое добавляется в конец элемента.

Проясним на примере:

<button hx-get="/fetch-data" hx-target="#data-container">
Fetch Data
</button>
<div id="data-container"></div>

В этом коде при нажатии кнопки:

  1. Из htmx в /fetch-data отправляется GET-запрос.
  2. В конечной точке серверной части данные извлекаются и отбражаются как HTML.
  3. Ответ вставляется в элемент #data-container.

Создание и удаление пользователя

Для создания этого простого приложения понадобятся такие инструменты/фреймворки:

  • Gin;
  • Tailwind CSS;
  • htmx.

Базовая настройка

Создадим в корневом каталоге файл main.go:

package main

import (
"fmt"
"github.com/gin-gonic/gin"
)

func main() {
router := gin.Default()

router.Run(":8080")
fmt.Println("Server is running on port 8080")
}

Им настраивается базовый сервер Go, запускаемый через порт **8080. 
Приложение запускается командой go run main.go.

  • Для отображения списка пользователей создаем в корневом каталоге HTML-файл:

users.html

<!DOCTYPE html>
<html>
<head>
<title>Go + htmx app </title>
<script src="https://unpkg.com/[email protected]" integrity="sha384-wS5l5IKJBvK6sPTKa2WZ1js3d947pvWXbPJ1OmWfEuxLgeHcEbjUUA5i9V5ZkpCw" crossorigin="anonymous"></script>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="text-center flex flex-col w-full gap-6 mt-10">
<table id="user-list" class="w-1/2 mx-auto mt-4 border border-gray-300">
<thead>
<tr class="border border-gray-300">
<th class="px-4 py-2">Name</th>
<th class="px-4 py-2">Email</th>
<th class="px-4 py-2">Actions</th>
</tr>
</thead>
<tbody>
{{ range .users }}
<tr class="border border-gray-300">
<td class="px-4 py-2">{{ .Name }}</td>
<td class="px-4 py-2">{{ .Email }}</td>
<td class="px-4 py-2">
<button class="bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-2 rounded">Delete</button>
</td>
</tr>
{{ end }}
</tbody>
</table>
</body>
</html>

Мы включили:
htmx при помощи тега script;
Tailwind CSS cdn-ссылкой.

Теперь воспользуемся классами CSS Tailwind и отобразим шаблоны при помощи htmx.

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

Выборка пользователей

main.go

package main

import (
"fmt"
"net/http"
"text/template"

"github.com/gin-gonic/gin"
)

func main() {
router := gin.Default()

router.GET("/", func(c *gin.Context) {
users := GetUsers()

tmpl := template.Must(template.ParseFiles("users.html"))
err := tmpl.Execute(c.Writer, gin.H{"users": users})
if err != nil {
panic(err)
}
})

router.Run(":8080")
fmt.Println("Server is running on port 8080")
}

type User struct {
Name string
Email string
}

func GetUsers() []User {
return []User{
{Name: "John Doe", Email: "[email protected]"},
{Name: "Alice Smith", Email: "[email protected]"},
}
}

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

Вот и все. Перезапускаем сервер и проверяем, отображается ли список пользователей. Такой, например, список:

Отображенный список пользователей

Создание пользователя

  • Создаем файл user_row.html, которым в таблицу user добавляется строка нового пользователя:

user_row.html

<tr class="border border-gray-300">
<td class="px-4 py-2">{{ .Name }}</td>
<td class="px-4 py-2">{{ .Email }}</td>
<td class="px-4 py-2">
<button class="bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-2 rounded">Delete</button>
</td>
</tr>

Структура здесь та же, что у строки таблицы user в users.html, так как для новой добавляемой строки нужен такой же стиль.

  • Меняем users.html, добавляя над тегами <table></table> такой код:
<form hx-post="/users" hx-target="#user-list" hx-swap="beforeend">
<input type="text" name="name" placeholder="Name" class="border border-gray-300 p-2 rounded">
<input type="email" name="email" placeholder="Email" class="border border-gray-300 p-2 rounded">
<button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Add User</button>
</form>

hx-post=“/users”  —  когда форма отправится, на маршрут /users активируется post-запрос.

**hx-target=“#user-list”  —  **указывается цель, куда добавляются данные.

hx-swap=“beforeend”  —  указывается позиция, куда добавляются данные. Мы добавляем нового пользователя в конец списка, поэтому использовали значение beforeend.

  • Реализуем POST-маршрут /users в файле main.go:
router.POST("/users", func(c *gin.Context) {
tmpl := template.Must(template.ParseFiles("user_row.html"))

name := c.PostForm("name")
email := c.PostForm("email")

user := User{Name: name, Email: email}
err := tmpl.Execute(c.Writer, user)
if err != nil {
panic(err)
}
})

Из формы ввода берутся name и email, и user_row.html выполняется.

Попробуем добавить в таблицу нового пользователя. Переходим сюда и нажимаем кнопку Add User:

Добавляем нового пользователя в список

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Nidhi D: Golang + htmx + Tailwind CSS: Create a Responsive Web Application

Предыдущая статьяC++: подробное руководство по циклам for с векторами
Следующая статья5 крайне непродуктивных моделей поведения владельцев продуктов