Полноценная веб-разработка с использованием HTMX и Bun, часть 2: шаблоны Pug

автор


В первой половине этой статьи мы настроили стек веб-разработки и создали простой пример приложения с использованием Bun, HTMX, Elysia и MongoDB. Здесь мы продолжим изучение нашего нового стека, очищая и абстрагируя уровень доступа к данным примера приложения и добавляя более сложные взаимодействия HTMX. Мы также добавим еще один компонент в набор технологий: Pug, популярный механизм шаблонов JavaScript, который хорошо работает с HTMX и помогает в настройке взаимодействия с DOM.

Пример приложения

Наш пример приложения на данный момент состоит из формы и таблицы. Форма позволяет пользователям вводить цитаты вместе с их авторами, которые затем можно искать и отображать с помощью пользовательского интерфейса приложения. Я добавил в интерфейс немного CSS, чтобы он выглядел более современно, чем то, на чем мы остановились в первой части:

Новый интерфейс HTMX с CSS. ИДГ

Вот внешний код обновленного интерфейса:


<script src="https://unpkg.com/htmx.org@1.9.10"></script>
  <form hx-post="/add-quote" hx-swap-oop="beforeend:#data-list" hx-trigger="every time">
    <input type="text" name="quote" placeholder="Enter quote">
    <input type="text" name="author" placeholder="Enter author">
    <button type="submit">Add Quote</button>
</form>
  <ul id="data-list"></ul>
  <button hx-get="/quotes" hx-target="#data-list">Load Data</button>

Мы используем HTMX для управления процессом отправки формы и загрузки данных в таблицу. Я также очистил серверную часть приложения, поэтому подключение к базе данных теперь является общим. Вот эта часть src/index.ts:


import { Elysia } from "elysia";
import { staticPlugin } from '@elysiajs/static';
const { MongoClient } = require('mongodb');

// Database connection details
const url = "mongodb://127.0.0.1:27017/quote?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.8.0";
const dbName = "quote";
const collectionName = "quotes";
let client = new MongoClient(url, { useUnifiedTopology: true });

// Connect to the database (called only once)
async function connectToDatabase() {
  try {
    await client.connect();
  } catch (error) {
    console.error(error);
    throw error; // Re-throw the error to indicate connection failure
  }
  return { client, collection:  client.db(dbName).collection(collectionName) };
}

// Close the database connection
async function closeDatabaseConnection(client) {
  await client.close();
}

Здесь мы определяем URL-адрес базы данных как адрес локального хоста MongoDB по умолчанию, а также имя базы данных и коллекции. Затем мы используем async функция, connectToDatabase(), чтобы подключить клиента и вернуть его подключенным к коллекции. Наш код может затем вызывать этот метод всякий раз, когда ему потребуется доступ к базе данных, и когда это будет сделано, он может вызвать client.close().

Использование подключения к базе данных

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


// Close the database connection
async function closeDatabaseConnection(client) {
  await client.close();
}

async function getAllQuotes(collection) {
  try {
    const quotes = await collection.find().toArray();

    // Build the HTML table structure
    let html="<table border="1">";
    html += '<tr><th>Quote</th><th>Author</th></tr>';
    for (const quote of quotes) {
      html += `<tr><td>${quote.quote}</td><td>${quote.author}</td></tr>`;
    }
    html += '</table>';

    return html;
  } catch (error) {
    console.error("Error fetching quotes", error);
    throw error; // Re-throw the error for proper handling
  }
}

// Main application logic
const app = new Elysia()
  .get("https://www.infoworld.com/", () => "Hello Elysia")
  .get("/quotes", async () => {
    try {
      const { client, collection } = await connectToDatabase();
      const quotes = await getAllQuotes(collection);
      await closeDatabaseConnection(client);
      return quotes;
    } catch (error) {
      console.error(error);
      return "Error fetching quotes";
    }
  })
  .use(staticPlugin())
  .listen(3000);

console.log(
  ` Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

Это дает нам серверную часть /quotes Конечная точка GET, к которой мы можем обратиться, чтобы получить данные котировок. Конечная точка вызывает getAllQuotes() метод, который использует коллекцию из connectToDatabase() чтобы получить массив цитат и авторов. Затем он генерирует HTMX для строк.

Наконец, мы отправляем ответ, содержащий строки в формате HTMX, и строки вставляются в таблицу.

Добавьте шаблонизатор Pug

Создание строки HTMX вручную может вызвать разочарование и ошибки. Механизм шаблонов позволяет нам определить структуру HTMX в вечном файле с чистым синтаксисом.

Самый популярный движок шаблонов HTML для JavaScript — Pug. Его использование сделает создание представлений на сервере намного проще и масштабируемее, чем встраивание в код JavaScript. Основная идея состоит в том, чтобы взять наши объекты данных и передать их в шаблон, который применяет данные и выводит HTML. Разница здесь в том, что мы генерируем HTMX, а не HTML. Мы можем это сделать, потому что HTMX — это, по сути, HTML с расширениями.

Для начала добавьте в проект библиотеку Pug с помощью: $ bun add pug.

Когда это завершится, создайте новый каталог в корне проекта с именем /views: ($ mkdir views)затем добавьте новый файл с именем quotes.pug:


doctype html
h1 Quotes
table
  thead
    tr
      th Quote
      th Author
      th Actions
  tbody
    each quote in quotes
      tr(id=`quote-${quote._id}`)
        td #{quote.quote}
        td #{quote.author}
        td
          button(hx-delete=`/quotes/${quote._id}` hx-trigger="click" hx-swap="closest tr" hx-confirm="Are you sure?") Delete
          #{quote._id}

Pug использует отступы для обработки вложенных элементов. Атрибуты заключаются в круглые скобки. Обычный текст, например слово Удалить предоставляется как есть. Все это дает нам компактный способ описания HTML и/или HTMX. Посетите домашнюю страницу Pug, чтобы узнать больше о его синтаксисе.

Обратите внимание, что внутри строки нам нужно использовать ${}. #{} Синтаксис позволяет ссылаться на любые объекты данных, которые были внедрены в шаблон. Это похоже на интерполяцию токенов в такой среде, как React. Основная идея состоит в том, чтобы определить общую структуру HTML/HTMX, а затем предоставить шаблону переменные, на которые ссылается #{} и ${}.

Мы предоставляем переменные обратно на сервер /quotes конечная точка, которая использует getAllQuotes():


import pug from 'pug';
//...
async function getAllQuotes(collection) {
  try {
    const quotes = await collection.find().toArray();

    // Render the Pug template with the fetched quotes
    const html = pug.compileFile('views/quotes.pug')({ quotes });

    return html;
  } catch (error) {
    console.error("Error fetching quotes", error);
    throw error; // Re-throw the error for proper handling
  }
}

Итак, мы получаем цитаты из базы данных, затем компилируем шаблон Pug и передаем цитаты. Затем Pug выполняет работу по объединению HTML и данных. Общий поток составляет:

  • Запрос поступает на GET /quotes.
  • Котировки извлекаются из MongoDB.
  • Шаблон Pug получает котировки.
  • Шаблон Pug отображает кавычки в формате HTML и/или HTMX.
  • Заполненный HTML и/или HTMX отправляется в качестве ответа.

Результирующий экран выглядит примерно так:

Интерфейс HTMX с шаблонами Pug ИДГ

Взаимодействия с DOM: удаление строки

Теперь нам нужно заставить нашу кнопку «Удалить» работать. Просто выпустив delete Запросить и обработать его на сервере и в базе данных легко, как мы уже видели, но как насчет обновления таблицы, чтобы отразить изменения?

Есть несколько способов подойти к обновлению. Мы могли бы просто обновить всю таблицу или использовать JavaScript или HTMX для удаления строки из таблицы. В идеале мы хотели бы использовать последний вариант и сохранить все в формате HTMX.

В нашем views/quotes.pug шаблон, мы можем использовать чистый HTMX для удаления строки:


  tbody(hx-target="closest tr" hx-swap="outerHTML")
    each quote in quotes
      tr(id=`quote-${quote._id}`)
        td #{quote.quote}
        td #{quote.author}
        td
          button(hx-delete=`/quotes/${quote._id}` hx-trigger="click" hx-confirm="Are you sure?") Delete

Существенными частями здесь являются hx-target=”closest tr” и hx-swap=”outerHTML” на tbody. ( hx-confirm позволяет вам предоставить confirm диалог.) hx-target говорит заменить ближайший tr к триггерному элементу (кнопке) с ответом. outHTML в hx-swap гарантирует удаление всего элемента строки таблицы, а не только его содержимого. На стороне сервера мы возвращаем успешный ответ (HTTP 200) с пустым телом, поэтому HTMX просто удалит строку:


async function deleteQuote(collection, quoteId) {
  try {
    const result = await collection.deleteOne({ _id: new ObjectId(quoteId) });
    if (result.deletedCount === 1) {
      return "";
    } else {
      throw new Error( "Quote not found");
    }
  } catch (error) {
    console.error("Error deleting quote", error);
    throw error; // Re-throw the error for proper handling
  }
}

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

Заключение

Хотя это руководство, состоящее из двух частей, включает в себя новые технологии, такие как Bun и Elysia, наиболее заметным компонентом является HTMX. Это действительно меняет способ работы приложения по сравнению с обычными API-интерфейсами JSON.

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

Для взаимодействия с DOM HTMX предлагает гибкую функциональность «из коробки» через hx-swap и hx-target. Для более сложных случаев использования вы всегда можете использовать JavaScript.

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

Вы можете найти код этого руководства в моем репозитории GitHub.

Дальше читайте это:

  • Почему компании уходят из облака
  • 5 простых способов запустить LLM локально
  • Программирование с помощью ИИ: советы и лучшие практики от разработчиков
  • Знакомьтесь, Zig: современная альтернатива C
  • Что такое генеративный ИИ? Искусственный интеллект, который создает
  • Лучшее программное обеспечение с открытым исходным кодом 2023 года

Related Posts

Оставить комментарий