Добавление внешнего интерфейса SvelteJS

Введение

Надеемся, что вы последовали этому примеру и создали работающий API списка дел, полностью поддерживаемый базой данных, а также с надежной структурой модульного тестирования. Если нет, вы можете вернуться к части 1.

Давайте закончим все это с интерфейсом, чтобы сделать его законченным приложением.

SvelteJS

Точно так же, как я не пытался объяснить предысторию Quarkus, я не собираюсь объяснять и SvelteJS. Если вы хотите прочитать об этом, просто зайдите на svelte.dev, чтобы прочитать все об этом. Тем не менее, что мне нравится в Svelte, так это то, что он компилируется в собственный JavaScript, который обеспечивает сверхбыструю производительность и чрезвычайно оптимизированный размер загрузки. Я также нахожу его интуитивно понятным и предпочитаю его альтернативам (по крайней мере, в настоящее время, и я часто меняю свое мнение о интерфейсных фреймворках).

Я использовал учебник от Freshman.tech, чтобы дать старт созданию приложения. Если вы заинтересованы в более подробном изучении Svelte, перейдите по ссылке https://freshman.tech/svelte-todo/.

Интеграция внешнего интерфейса с Quarkus

Следует отметить, что Quarkus по умолчанию применяет CORS, поэтому ожидает, что запросы внешнего интерфейса будут поступать из того же домена (который включает порт) приложения Quarkus. Чтобы достичь этого, мы можем сделать одну из нескольких вещей

  1. Скопируйте скомпилированное приложение SvelteJS в каталог ресурсов приложения Quarkus, чтобы оно обслуживалось из Quarkus.
  2. Запустите Quarkus / SvelteJS отдельно и поместите обратный прокси-сервер (например, Nginx) впереди, чтобы оба выглядели так, как будто они обслуживаются из одного и того же.
  3. Отключить CORS

Обычно я рекомендую вариант 2, так как он достаточно прост, а также позволяет запускать Quarkus и Svelte в режиме разработки с горячей перезагрузкой. Однако для нашего приложения мы выберем вариант 1, так как это самый простой подход, и в нем не задействована другая технология. Я не рекомендую вариант 3, потому что CORS — лучшая практика для веб-приложений.

Давайте начнем!

Svelte использует NPM для настройки и запуска необходимого набора инструментов. Итак, в корне нашего проекта давайте запустим следующее, чтобы настроить его.

npx degit sveltejs/template todo-frontend
cd todo-frontend/
npm install

Если бы мы использовали вариант 2 выше, мы могли бы сейчас перейти в режим разработки и начать взламывать наш код. Для этого мы запустим npm run dev , но перейдем в наш редактор кода и, когда закончим, создадим скомпилированную версию нашего приложения.

Нам нужно перейти к todo-frontend\src\App.svelte и открыть его в нашем редакторе. Нам не нужно будет редактировать какие-либо другие файлы в рамках нашего руководства (если вас не беспокоит заголовок по умолчанию, который вы можете изменить в файле public\index.html.

Компонент Svelte (наше приложение будет единым инкапсулированным компонентом) имеет три раздела в файле: скрипт, стиль и основной (куда идет наш HTML). Удалите содержимое файла App.svelte и добавьте следующий код.

<style>
  .todo-list { list-style: none; margin-bottom: 20px;}
  .done span { text-decoration: line-through; 
  .done .tick::before { display: inline; }
  .tick::before { content: '✓'; font-size: 20px; display: none; }
  .tick {
    width: 30px; height: 30px; border: 3px solid #333; 
    border-radius: 50%; display: inline-flex; 
    justify-content: center; align-items: center; cursor: pointer;
  }
  .delete-todo {
    border: none; background-color: transparent; outline: none; 
    cursor: pointer;
  }
</style>
<main>
  <div class="container">
    <h1 class="app-title">My Todos</h1>
    <ul class="todo-list">
      {#each todoItems as todo}
        <li class="todo-item {todo.completed ? 'done' : ''}">
          <label class="tick" on:click={() => toggleDone(todo.id)}></label>
          <span>{todo.txt}</span>
          <button class="delete-todo" on:click={() => deleteTodo(todo.id)}> X </button>
        </li>
      {/each}
    </ul>
    <form on:submit|preventDefault={addTodo}>
      <input class="js-todo-input" type="text" aria-label="Enter a new todo" placeholder="Enter your todo" bind:value={newTodo}>
    </form>
  </div>
</main>

Если вы знакомы с интерфейсными фреймворками, это должно выглядеть достаточно знакомо. Стиль настроен на зачеркивание текста и отображение галочки внутри круглого флажка (метка с радиусом 50%). CSS устанавливается с использованием кода стиля Handlebars { somelogic ? "output if true":"else output if false" }, а события управляются двумя разными способами.

  1. При нажатии на метку или кнопку выполняется метод, использующий подход on:click=, который затем выполняет функцию, которую мы создадим в следующем бите.
  2. При нажатии клавиши Enter выполняется {addTodo} в результате on:submit. PreventDefault предотвращает отправку формы через стандартный HTML. Однако, когда вызывается функция addTodo, она считывает значение из newTodo, которое было автоматически обновлено из-за ошибки bind:value={newTodo}. Значение привязки гарантирует, что любые изменения на входе автоматически обновляются в значении, хранящемся в javascript. Это делается с помощью скомпилированного кода, а не через shadowDOM, как в других популярных фреймворках (Angular, React, VUE), и именно поэтому Svelte очень мало использует память.

Итак, теперь, когда у нас есть код HTML и CSS, пришло время добавить логику внешнего интерфейса. В верхней части того же файла добавьте следующий код.

<script>
// Svelte code based on https://freshman.tech/svelte-todo/
import { afterUpdate } from ‘svelte’;
import { onMount } from “svelte”;
let todoItems = [];
let newTodo = ‘’;
onMount(async function() {
  const response = await fetch(“/todos”);
  const json = await response.json();
  todoItems = json;
});
afterUpdate(() => {
  document.querySelector(‘.js-todo-input’).focus();
});
</script>

Этот код создает две переменные, необходимые для нашего приложения. Первый — это список задач, а второй — переменная связывания для поля ввода.

Следующая функция считывает текущие задачи (getAll) из нашего API, созданного в частях 1 и 2, и сохраняет их в списке todoItems.

И, наконец, функция afterUpdate гарантирует, что фокус возвращается к полю ввода после изменения состояния, поэтому нам не нужно снова нажимать на поле ввода.

Далее нам нужно добавить функции addTodo, toggleDone, deleteTodo. Итак, перед закрывающим тегом script добавьте оставшийся код.

async function addTodo() {
  newTodo = newTodo.trim();
  if (!newTodo) return;
  const response = await fetch("/todos", { method: 'POST', headers: {'Content-Type': 'application/json'}, body: newTodo });
  const todo = await response.json();
  todoItems = […todoItems, todo];
  newTodo = '';
}
function toggleDone(id) {
  const index = todoItems.findIndex(item => item.id === Number(id));
  todoItems[index].completed = !todoItems[index].completed;
  update(todoItems[index])
}
async function update(todo) {
   const response = await fetch("/todos/"+todo.id, { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(todo) });
   const updatedTodo = await response.json();
   todo = updatedTodo;
}
async function deleteTodo(id) {
  const response = await fetch("/todos/"+id, { method: 'DELETE' });
  const success = await response.ok;
  if (success) todoItems = todoItems.filter(item => item.id !== Number(id));
}

Опять же, этот код должен выглядеть как старый добрый Javascript, так что обсуждать особо нечего. Функция addTodo проверяет наличие данных, а затем вызывает оператор выборки для нашего API Quarkus, используя HTTP POST. Когда полный Todo (включая идентификатор) возвращается, он добавляется в список todoItems и, наконец, связывание newTodo очищается, так что мы можем добавить наш следующий Todo.

DeleteTodo просто вызывает метод HTTP DELETE, снова используя оператор Fetch. Если ответ успешен, мы обновляем список, отфильтровывая удаленные задачи.

ToggleDone работает, переключая значение todo.completed, а затем отправляя его в функцию обновления. Функция обновления просто берет задачу, выполняет HTTP PUT, а затем обновляет локальную задачу ответом от сервера. Причина разделения Toggle на две функции заключается в том, что если мы добавим будущую функцию для обновления текста задачи, функцию обновления можно будет использовать совместно.

Собираем все вместе

Теперь, когда наш код написан, нам нужно упаковать код и скопировать его в наше приложение Quarkus, чтобы мы могли запустить его вместе. Для этого нам нужно запустить следующую команду из каталога todo-frontend.

npm run build

Это скомпилирует и упакует все приложение Svelte. Теперь нам просто нужно скопировать каталоги index.html и build в src/main/resources/META-INF/resources и повторно запустить приложение Quarkus (mvn quarkus:dev), и наше приложение Todo должно быть готово!!