Создание сайта Svelte ecom

Эту статью подготовил Алекс Попуцис.

Сегодня я рассмотрю, как можно объединить две новые, быстро развивающиеся веб-технологии, чтобы быстро создать веб-приложение и настроить его на бесконечное масштабирование.

Эти две технологии — Svelte и Fauna. Svelte — это одна из новейших технологий в мире разработки фронтенда, основанная на том, что уже сделали React, Angular, Vue и другие, но при этом значительно упрощающая ситуацию. Если вы новичок в Svelte, я рекомендую начать с их учебника здесь. Fauna — это современная, основанная на документах, бессерверная база данных, которая работает в облаке и предлагает щедрый бесплатный уровень для начала работы. Вот ссылка на документацию Fauna, если вам нужно ознакомиться с ней, прежде чем приступать к этому руководству.

В этом учебнике будут рассмотрены все основы, необходимые для создания небольшого сайта электронной коммерции с использованием Fauna и Svelte. Вы узнаете:

  • Как создать базу данных Fauna и наполнить ее коллекциями, индексами и документами.
  • Основные принципы проектирования баз данных Fauna и язык запросов Fauna Query Language (FQL)
  • Как подключить приложение Svelte к Fauna и отображать данные
  • Как реализовать базовую корзину для приложения электронной коммерции Svelte
  • Как записывать данные обратно в Fauna из нашего приложения Svelte

Начало работы

Перейдите на сайт fauna.com и зарегистрируйте новую учетную запись. На приборной панели нажмите кнопку Создать базу данных. Наш сайт электронной коммерции будет предназначен для пекарни, поэтому я назвал свою базу данных SvelteBakery и поместил ее в группу регионов США:

Как только база данных будет запущена, мы создадим пару коллекций — эквивалент таблицы в традиционной реляционной базе данных. Я выбрал значение по умолчанию — 30 дней сохраненной истории для каждого документа (эквивалент строки) в коллекции, и TTL не задан. Указание TTL приведет к удалению записей, которые оставались нетронутыми в течение определенного количества дней. Я создаю одну коллекцию для категорий товаров, одну для самих товаров и одну для хранения данных о заказах.

Далее я собираюсь вставить некоторые данные в мою базу данных. Я начну с коллекции Categories, щелкну в ней, а затем нажму New Document. Если вы знакомы с JSON, это будет довольно просто, вы можете поместить любой JSON, который хотите, внутрь скобок. Например, чтобы добавить категорию под названием «Десерт», вы можете сделать следующее:

Вы также можете использовать оболочку (либо внутри приборной панели Fauna, либо с помощью пакета fauna-shell NPM) или любой язык, поддерживающий API Fauna, который я использовал в данном случае для массового добавления элементов:

Create(Collection("Categories"), { data: { name: "Sandwiches" } })

Create(Collection("Categories"), { data: { name: "Sides" } })

Create(Collection("Categories"), { data: { name: "Beverages" } })

Create(Collection("Categories"), { data: { name: "Desserts" } })
Войти в полноэкранный режим Выход из полноэкранного режима

Мы получим обратно вставленные данные, с уникальной ссылкой для каждого, которую нам нужно будет использовать позже для добавления элементов:

[
  {
    ref: Ref(Collection("Categories"), "322794748885598273"),
    ts: 1644099911490000,
    data: {
      name: "Sandwiches"
    }
  },
  {
    ref: Ref(Collection("Categories"), "322794748889792577"),
    ts: 1644099911492000,
    data: {
      name: "Sides"
    }
  },
  {
    ref: Ref(Collection("Categories"), "322794748889793601"),
    ts: 1644099911495000,
    data: {
      name: "Beverages"
    }
  },
  {
    ref: Ref(Collection("Categories"), "322794748889794625"),
    ts: 1644099911497000,
    data: {
      name: "Desserts"
    }
  }
]
Войти в полноэкранный режим Выход из полноэкранного режима

Далее давайте создадим товар. Перейдите в коллекцию Products и начнем с сэндвича BLT:

Название и цена выглядят довольно просто, но что происходит с этой категорией? Это то, что в Fauna называется ссылкой. По сути, мы говорим базе данных обратиться к коллекции Categories и вернуть документ с идентификатором 322794748885598273.

Использование идентификатора в качестве ссылки вместо простого добавления названия категории упрощает последующее изменение сведений о категории. Например, если я захочу изменить название категории с «Напитки» на «Напитки», я смогу сделать это в коллекции Categories, не обращаясь к коллекции Products.

Далее я собираюсь выполнить массовое добавление товаров в базу данных. Я сделаю это через оболочку Fauna, используя функции Fauna Map и Lambda:

Map(
    [
        ['BLT', 6.99, '322794748885598273'],
        ['Turkey and Swiss', 7.99, '322794748885598273'],
        ['Egg, Bacon, and Cheese', 4.99, '322794748885598273'],
        ['Chicken Bacon Ranch', 7.99, '322794748885598273'],
        ['Grilled Cheese', 5.99, '322794748885598273'],
        ['Potato Chips', 1.25, '322794748889792577'],
        ['Mac and Cheese', 3.99, '322794748889792577'],
        ['Potato Salad', 2.99, '322794748889792577'],
        ['Soup of the Day', 3.99, '322794748889792577'],
        ['Bottled Water', 1.50, '322794748889793601'],
        ['Fountain Drink', 1.99, '322794748889793601'],
        ['Coffee', 1.99, '322794748889793601'],
        ['Cold Brew', 2.99, '322794748889793601'],
        ['Espresso', 3.99, '322794748889793601'],
        ['Blueberry Muffin', 1.99, '322794748889794625'],
        ['Chocolate Chip Muffin', 1.99, '322794748889794625'],
        ['Croissant', 1.49, '322794748889794625'],
        ['Brownie', 1.49, '322794748889794625'],
        ['Chocolate Chip Cookie', .99, '322794748889794625'],
        ['Slice of Cheesecake', 2.99, '322794748889794625']
    ],

    Lambda(
        ['name', 'price', 'category'],
        Create(
            Collection('Products'),
            {
                data: {
                    name: Var('name'),
                    price: Var('price'),
                    category: Ref(Collection('Categories'), Var('category'))
                }
            }
        )
    )
)
Войти в полноэкранный режим Выход из полноэкранного режима

Если вы знакомы с функцией map в JavaScript, это может показаться вам знакомым. По сути, я создал массив массивов, а функция map перебирает внешний массив и передает каждый внутренний массив в функцию Lambda для обработки. Лямбда разрушает внутренний массив на составляющие: название, цену и идентификатор категории. Затем я беру эти данные и передаю их в функцию Create, чтобы добавить новый документ в коллекцию Products. Это позволило мне добавить все эти строки менее чем за полсекунды и избавило меня от лишнего набора текста.

Далее я собираюсь создать несколько индексов, чтобы помочь в поиске данных. Во-первых, нам понадобится индекс для all_categories, поскольку именно так мы будем группировать наше меню во фронтенде. Выполните следующие действия в консоли Fauna:

CreateIndex({
    name: "all_categories",
    source: Collection("Categories"),
    values: [
        { field: ["ref"] },
        { field: ["data", "name"] }
    ]
})
Войти в полноэкранный режим Выйти из полноэкранного режима

Это создаст индекс all_categories, который будет возвращать ID и название каждой категории. Я перечислил ссылку (ID) первой, потому что мы будем использовать ее для сортировки.

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

Теперь, когда я возвращаюсь к этому индексу, я могу использовать опцию FQL для поиска в нем. На скриншоте фрагмент кода обрезан, но это просто Ref(Collection('Categories'), '322794748885598273').

Это вернет название и цену для каждого товара в категории 322794748885598273, которая называется «Бутерброды».

Безопасность

Есть еще одна вещь, которую нам нужно сделать на стороне базы данных, прежде чем мы перейдем к созданию нашего приложения. Нам нужно будет создать роль в Fauna, которую мы сможем использовать для получения данных. На вкладке безопасности в приборной панели выберите Роли и нажмите Новая пользовательская роль. Я использовал здесь название Svelte, но вы можете выбрать любое другое. Затем добавьте все три коллекции и индексы с помощью выпадающих окон. Дайте товарам и категориям разрешение на чтение, установив флажок в этом столбце, а заказам дайте разрешение на создание. Затем дайте обоим индексам разрешение на чтение.

Здесь мы говорим Fauna, что хотим получить доступ на чтение к нашим продуктам и категориям, чтобы заполнить меню на фронтенде. Мы также хотим иметь возможность записывать новые заказы в коллекцию заказов, когда заказ отправлен. Наконец, мы хотим иметь доступ только для чтения к нашим индексам. Мы не хотим предоставлять этой роли доступ для выполнения каких-либо неблаговидных действий, например, создания новых продуктов в коллекции Products или удаления Categories, поэтому здесь важно быть очень разборчивыми.

После этого вернитесь на вкладку Security, выберите Keys и создайте новый ключ, используя роль Svelte:

Вы получите ключ обратно, когда отправите его; храните его в безопасном месте, потому что вы увидите его только один раз.

Создание фронтенда

Теперь у нас достаточно настроек в базе данных, чтобы начать работу над нашим приложением Svelte. В Visual Studio Code я открыл созданную мной папку под названием SvelteBakery, а затем выполнил следующие команды терминала, чтобы запустить свой проект:

npx degit sveltejs/template .
npm install
Войти в полноэкранный режим Выйти из полноэкранного режима

Внутри папки src я также создал вложенную папку components и вложенную папку stores. Структура папок должна выглядеть следующим образом:

Начнем с папки stores. Здесь мы создадим файл stores.js и заполним его следующим кодом:

import { writable } from ‘svelte/store’

export const shoppingCart = writable([])
Вход в полноэкранный режим Выйти из полноэкранного режима

Svelte позволяет очень легко обмениваться данными между компонентами с помощью этих встроенных хранилищ. Если вы пришли из другого фреймворка, например, React, вы, возможно, привыкли использовать что-то вроде Redux для управления состоянием, но Svelte включает это из коробки. Мы будем использовать этот магазин позже в учебнике для отслеживания товаров в нашей корзине.

Я также создаю магазин, чтобы облегчить совместное использование нашего клиента Fauna в разных компонентах. Для этого я создал файл fauna.js в той же папке stores и заполнил его похожим кодом:

import { writable } from 'svelte/store'

export const fauna = writable({})
Вход в полноэкранный режим Выйти из полноэкранного режима

Единственная реальная разница заключается в том, что shoppingCart был инициализирован пустым массивом, а магазин fauna был инициализирован пустым объектом.

Я собираюсь использовать это в нашем основном файле App.svelte, который теперь выглядит следующим образом:

<script>
    import ProductMenu from "./components/ProductMenu.svelte";
    import ShoppingCart from "./components/ShoppingCart.svelte";
    import Nav from "./components/Nav.svelte";
    import { fauna } from './stores/fauna';

    let currentPage = "menu";

    function connectToFauna() {
        connectToFauna = () => {};
        fauna.set(
            {
                client: new window.faunadb.Client({ domain: 'db.us.fauna.com', scheme: 'https', secret: "INSERT_SECRET_HERE" }),
                q: window.faunadb.query
            }
        )
    }

    function handlePageChange(e) {
        currentPage = e.detail.newPage;
    }
</script>

<main>
    <Nav on:changepage={handlePageChange} />
    {#if currentPage === "menu"}
        <ProductMenu />
    {:else if currentPage === "cart"}
        <ShoppingCart />
    {/if}
</main>

<svelte:head>
    <script src="//cdn.jsdelivr.net/npm/faunadb@latest/dist/faunadb-min.js" on:load={connectToFauna}></script>
</svelte:head>

<style>
    main {
        text-align: center;
        padding: 1em;
        margin: 0 auto;
    }
</style>
Вход в полноэкранный режим Выйти из полноэкранного режима

Здесь нужно отметить несколько моментов:

  • В верхней части у меня есть четыре импорта: ProductMenu, ShoppingCart, и Nav (три компонента Svelte, которые мы создадим ниже), и наш клиентский магазин Fauna, который мы создали выше.
  • Ниже я использую блок svelte:head. Это позволяет вставлять код в раздел head скомпилированного HTML. В данном случае я решил использовать драйвер Fauna JavaScript из CDN, а не пытаться установить его из npm и работать с полифиллом.
  • У меня есть параметр onload, так что после загрузки драйвера Fauna функция connectToFauna будет запущена и создаст соединение. Заслуга этой функции принадлежит Svelte REPL, которую я нашел здесь.
  • Одна проблема, с которой я столкнулся, связана с доменом. В зависимости от того, в каком регионе находится ваша база данных Fauna, вам нужно будет настроить это соответствующим образом — каждая группа регионов имеет свой собственный поддомен, который вы должны использовать, иначе вы будете в недоумении, почему вы не можете получить доступ к своим данным.
  • В дополнение к домену вам нужно будет вставить секретный ключ, созданный ранее для роли базы данных Svelte. Заполните его там, где написано «INSERT_SECRET_HERE».
  • У меня есть функция handlePageChange и блок if в самом HTML, чтобы определить, отображать ли меню или корзину. Подробнее об этом я расскажу ниже.

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

  • Nav.svelte
  • ProductMenu.svelte
  • MenuSection.svelte
  • MenuItem.svelte
  • ShoppingCart.svelte

Начнем с компонента Nav, поскольку он позволит нам переходить от меню товара к корзине и обратно. Вот полный код для Nav:

<script>
    import { shoppingCart } from '../stores/shoppingCart';
    import { createEventDispatcher } from 'svelte';

    const dispatch = createEventDispatcher();

    function changePage(newPage) {
        if (newPage === "menu" || $shoppingCart.length > 0)
            dispatch('changepage', {
                newPage: newPage
            });
    }
</script>

<nav>
    <h1>Svelte Bakery Demo</h1>
        <a href="#" on:click={() => changePage("menu")}>Menu</a>
        <a href="#" on:click={() => changePage("cart")}>Cart
            {#if $shoppingCart.length > 0}({$shoppingCart.length} items){/if}
        </a>
</nav>

<style>
    nav {
        display: flex;
        color: #FFF;
        background-color: rgb(14, 14, 46);
        flex-direction: column;
        align-items: center;
    }

    @media(min-width: 768px) {
        nav {
            flex-direction: row;
            gap: 3rem;
        }
    }

    h1 {
        flex-basis: 50%;
    }

    a {
        text-decoration: none;
        color: #FFF;
        font-weight: bold;
    }

    a:hover {
        text-decoration: underline;
    }
</style>
Вход в полноэкранный режим Выход из полноэкранного режима

В разделе сценария я использую createEventDispatcher для отправки пользовательского события родителю (App.svelte). При нажатии на одну из ссылок вызывается changePage, и эта функция посылает событие главному приложению с новым именем страницы. Вспомните этот код выше из App.svelte, когда мы поместили наш компонент Nav в HTML:

<Nav on:changepage={handlePageChange} />
Войти в полноэкранный режим Выход из полноэкранного режима

Вы можете видеть, что я настроил его на вызов функции handlePageChange в App.svelte при отправке события changepage. Это обновит переменную currentPage в App.svelte, которая определяет, видит ли конечный пользователь меню или корзину.

В дополнение к функциональности, связанной с изменением страниц, вы также можете увидеть еще один блок if в Nav.svelte. Я импортировал свой магазин корзины и использую shoppingCart.length в нескольких местах, чтобы определить, показывать ли количество товаров в корзине в навигационной панели, а также должна ли ссылка на корзину вообще что-то делать — мы не будем направлять пользователя в корзину, если она в данный момент пуста. Переменные магазина должны иметь префикс со знаком доллара, чтобы получить к ним доступ, но в остальном они работают как обычные переменные для чтения, что очень упрощает работу.

Далее мы приступим к созданию собственно меню продуктов. Компонент ProductMenu будет нашей оберткой для меню, и он будет содержать компонент MenuSection для каждой категории, содержащей наши товары. Внутри каждого MenuSection будут отдельные компоненты MenuItem для каждого продукта. Давайте начнем с самого верхнего уровня, ProductMenu, и будем двигаться вниз:

<script>
    import MenuSection from './MenuSection.svelte';
    import { fauna } from '../stores/fauna';

    let loaded = false;
    let categories = [];

    $: {
        if ($fauna.client && !loaded) {
            loaded = true;
            $fauna.client.query(
                $fauna.q.Paginate(
                    $fauna.q.Match(
                        $fauna.q.Index('all_categories')
                    )
                )
            )
            .then((res) => {
                categories = res.data;
            })
        }
    }
</script>

<section>
    <h2>Menu</h2>
    {#each categories as category}
        <MenuSection {category} />
    {/each}
</section>
Вход в полноэкранный режим Выход из полноэкранного режима

Здесь, в ProductMenu.svelte, я импортирую наш клиент Fauna из его магазина, а также компонент MenuSection, который будет следующим уровнем ниже. Затем я инстанцировал пару переменных, одну для хранения состояния загрузки и одну для хранения списка категорий. Я также проверил состояние загрузки, потому что не хочу пытаться запрашивать Fauna, пока клиент не будет готов к работе.

Затем я использую реактивный блок ($: в Svelte указывает на блок кода, который будет выполняться, когда что-то внутри него изменится, в данном случае, когда изменится хранилище $fauna.client). Этот блок запросит индекс all_categories и вернет id и name каждой категории. Я заполняю массив categories этими возвращенными данными. Ниже я использую блок Svelte each для итерации по массиву категорий. Каждая категория порождает компонент MenuSection с категорией, переданной в качестве параметра. Давайте рассмотрим MenuSection дальше.

<script>
    import MenuItem from './MenuItem.svelte';
    import { onMount } from 'svelte';
    import { fauna } from '../stores/fauna';

    export let category;

    let [ ref, name ] = category;
    let items = [];

    onMount(async () => {
        let x = await $fauna.client.query(
            $fauna.q.Paginate(
                $fauna.q.Match(
                    $fauna.q.Index('product_by_category'), 
                    ref
                )
            )
        );
        items = x.data;
    })
</script>

<div>
    <h3>{name}</h3>
    <div class="items">
        {#each items as item}
            <MenuItem {item} />
        {/each}
    </div>
</div>

<style>
    .items {
        display: flex;
        gap: 2rem;
        justify-content: center;
        flex-wrap: wrap;
    }
</style>
Вход в полноэкранный режим Выход из полноэкранного режима

Этот код дает нам одну вещь, которую мы еще не видели в предыдущих компонентах Svelte: оператор export let. Export let определяет параметр, который передается в этот компонент его родителем. В данном случае мы определили категорию как то, что будет передано в компонент. Вы могли заметить, что компонент MenuSection был вставлен в меню ProductMenu с помощью <MenuSection {category} />. Поскольку переданная переменная имеет имя category, а компонент ожидает одно имя category, я смог заключить category в скобки и оставить все как есть. Если бы переменная имела другое имя в ProductMenu, например, c, мне пришлось бы набрать <MenuSection category={c} /> вместо этого. Этот приятный синтаксический сахар специфичен для Svelte.

Вернемся к компоненту MenuSection — здесь мы также используем onMount. Это один из методов жизненного цикла Svelte, который позволяет нам запускать код, когда компонент был смонтирован в DOM. В отличие от ProductMenu, мы знаем, что к монтированию MenuSection у нас будет инстанцированный клиент Fauna, поэтому нам не нужно использовать обходной путь реактивного оператора, который мы создали ранее. В этом случае мы можем чисто вызвать клиента Fauna внутри onMount.

Здесь я использую индекс product_by_category, который позволяет нам искать по category_id. Поскольку мы передали эти данные в компонент вместе с названием категории, мы можем сделать запрос, чтобы получить все продукты в определенной категории. Затем я использую еще один блок each для итерации и отправки каждого из них в компонент MenuItem. Посмотрите на последнюю часть меню, MenuItem:

<script>
    import { shoppingCart } from "../stores/shoppingCart";

    export let item;

    let [ name, price ] = item;

    function addToCart() {
        shoppingCart.update(n => {
            return [ ...n, {name: name, price: price}];
        });
    }
</script>

<div>
    <h4>{name}</h4>
    <strong> ${price.toFixed(2)}</strong>
    <button on:click={addToCart}>Add to cart</button>
</div>

<style>
    div {
        display: flex;
        flex-direction: column;
        width: 90%;
        border: 1px solid #000;
        border-radius: 5px;
    }

    @media(min-width: 768px) {
        div {
            width: 15%;
        }
    }

    button {
        width: fit-content;
        margin: 1rem auto;
    }
</style>
Вход в полноэкранный режим Выход из полноэкранного режима

В разделе сценария я импортировал магазин shoppingCart, так как здесь мы будем записывать товары в корзину по мере их добавления. У меня также снова есть объявление export let, так как конкретный объект элемента будет передан из MenuSection. Наконец, я определяю функцию addToCart, которая добавит товар в магазин shoppingCart с помощью метода shoppingCart.update.

HTML и CSS довольно просты; я лишь обращу особое внимание на on:click, объявленный для кнопки. Это синтаксис Svelte для размещения обработчика события на элементе. Поскольку название товара и цена привязаны к компоненту, мне не нужно беспокоиться о передаче параметров в функцию, так как они уже известны.

Мы приближаемся к концу! Теперь давайте рассмотрим процессы оформления заказа и корзины. Вот ShoppingCart.svelte:

<script>
import { shoppingCart } from "../stores/shoppingCart";
import { fauna } from '../stores/fauna';

const reducer = (previousValue, currentValue) => previousValue + parseFloat(currentValue.price);
let total;
let customerName;
let confirmed = false;

$: total = $shoppingCart.reduce(reducer, 0);

function removeItem(idx) {
shoppingCart.update(n => {
if (idx === 0) return [...n.slice(1)];
if (idx === n.length - 1) return [...n.slice(0, n.length - 1)]
return [...n.slice(0, idx), ...n.slice(idx+1)]
})
}

function submitOrder() {
console.log(customerName);

$fauna.client.query(
$fauna.q.Create(
$fauna.q.Collection('Orders'),
{ data: { customerName: customerName, details: $shoppingCart, total: total } }
)
).then(() => {
confirmed = true;
})
}
</script>

<section>
    <h2>Shopping Cart</h2>

    <table>
        <thead>
            <tr>
                <th>Item</th>
                <th>Price</th>
                <th></th>
            </tr>
        </thead>

        <tbody>
            {#each $shoppingCart as item, idx}
                <tr>
                    <td>{item.name}</td>
                    <td>${item.price.toFixed(2)}</td>
                    <td>
                        <button on:click={() => removeItem(idx)}>Remove</button>
                    </td>
                </tr>
            {/each}
        </tbody>

        <tfoot>
            <tr>
                <td>Total</td>
                <td>${total.toFixed(2)}</td>
                <td></td>
            </tr>
        </tfoot>
    </table>

    {#if !confirmed}
        <form on:submit|preventDefault={submitOrder}>
            <label for="name">Name: </label>
            <input type="text" id="name" bind:value={customerName} />
            <input type="submit" />
        </form>
    {:else}
        <div>Thank you very much for your order!</div>
    {/if}
</section>

<style>
    section, table {
        margin: 0 auto;
    }

    table {
        border: 1px solid #000;
        border-collapse: collapse;
    }

    td, th {
        padding: 1rem;
    }

    tr:nth-child(even) {
        background: rgb(172, 170, 170);
    }

    thead, tfoot {
        font-weight: bold;
    }
</style>
Вход в полноэкранный режим Выход из полноэкранного режима

На этот раз мы импортируем оба наших магазина, поскольку нам понадобится shoppingCart для чтения данных, которые мы положили в корзину, и нам понадобится драйвер Fauna для создания нового документа в коллекции Orders. Я использую редуктор для суммирования цен на все товары в корзине и получения одного итогового значения.

В разделе HTML я создал таблицу, в которой будут содержаться данные о моей корзине. В каждой строке также есть кнопка, вызывающая функцию removeItem, которая удалит этот товар из корзины. В отличие от блоков each, которые я использовал ранее, в этом блоке я также использовал индекс, так как это позволяет мне отслеживать индекс для каждой строки в таблице, чтобы я мог использовать это значение для удаления нужной записи из моего магазина shoppingCart.

Наконец, у меня есть условная секция, которая покажет либо форму заказа, если заказ не был отправлен, либо текст подтверждения, если он был отправлен. Отправка формы вызывает submitOrder, которая использует функцию Create в Fauna для добавления нового документа в нашу коллекцию Orders (напомню, что роль, которую мы создали ранее, позволяет это сделать). Я использую привязку формы Svelte для привязки ввода имени к переменной customerName, что делает работу с формами чрезвычайно удобной.

После тестового запуска корзины вот пример конечных данных в коллекции Orders:

{
    "ref": Ref(Collection("Orders"), "321462934244950082"),
    "ts": 1642829794070000,
    "data": {
        "customerName": "Alexander Popoutsis",
        "details": [
            {
                "name": "Potato Chips",
                "price": 1.25
            },
            {
                "name": "Potato Salad",
                "price": 2.99
            },
            {
                "name": "Soup of the Day",
                "price": 3.99
            }
        ],
        "total": 8.23
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Следующие шаги

Если вы хотите увидеть полный код в одном месте, он находится на GitHub.

Конечно, это базовая демонстрация, и есть много вещей, которые можно сделать более продвинутыми (например, обработка количества товаров и улучшение стиля). Кроме того, поскольку это полностью клиентское приложение, ключ Fauna виден конечным пользователям, а безопасность особенно важна. Чтобы добавить компонент на стороне сервера, вы можете использовать SvelteKit и спрятать части Fauna за конечной точкой API SvelteKit.

Теперь у вас есть все необходимое, чтобы начать работу с базовым приложением, используя фантастическое сочетание Fauna и Svelte. Если вы еще не сделали этого, нажмите здесь, чтобы зарегистрировать учетную запись Fauna. А если вы вдруг застрянете, сообщество Fauna — это фантастический ресурс.

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *