Реклама ООО Таймвэб
Реклама ООО Таймвэб

Создаем блог на Svelte. Часть 6: Авторство, теги, пагинация и поддержка markdown

Обсудить
Создаем собственный блог на Svelte. Часть 6: Авторство, тэги, пагинация и поддержка markdown

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

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

Предыдущий материал: Блог на Svelte. Часть 5: Многостраничный сайт, авторизация, редактирование статей

Список авторов и ссылки на них в статьях

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

Но перед началом надо слегка модифицировать компонент signUp. Нам нужно добавить туда два новых поля и привязать две новые переменные: name и nickname.

Новые параметры в интерфейсе регистрации

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

Так что добавим в функцию signUp еще и функцию передачи в базу данных параметра nickname

Надо отметить, что вам, скорее всего, придется создать это свойство в таблице Users самостоятельно, указав в отдельном поле пункты nickname и userName

Создаем список авторов

Начнем генерацию новых пользователей в таблице Users при помощи новой функции addUserToTable. Давайте разберем ее код:

Функция addUserToTable

• Указываем название самой функции и аргумент userInfoconst addUserToTable = async (userInfo) => 

• В теле функции делаем несложный запрос к базе данных, который будет заполнять таблицу Users: 

let data, error = await supabase.from(‘Users’).insert([userId: userInfo.id, userName: name, nickname: nickname])

Тут все, думаю, понятно без дополнительных комментариев. 

addUserToTable мы будем запускать внутри функции signUp. Сразу после переноса информации о пользователе в хранилище запускаем addUserToTable с аргументом userInfo. Так мы передадим в таблицу Users наиболее актуальную информацию о зарегистрировавшемся человеке. 

Готово. Теперь после каждой регистрации необходимая информация о пользователе будет попадать не только во внутреннюю БД Supabase, но и в нашу таблицу Users, которая ляжет в основу фильтрации материалов по их автору. 

Создаем страницу со списком авторов

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

Создаем файл authors.svelte и внутри пишем следующий код:

Код модифицируемового файла для загрузки постов

• Импортируем базу данных при помощи import supabase from ‘$lib/db’

• Импортируем onMount таким образом: import onMount from ‘svelte’

• Создаем пустой массив для пользователей из БД – let users = []

• Прописываем функцию onMount, в теле которой будет запрос к таблице Users:

 let data, error = await supabase.from(‘Users’).select(‘*)

• Значение переменной users меняем на список полученных пользователей из БД – users = data

И делаем простенький интерфейс для описанной выше логики.

С помощью блока #each перебираем все записи в таблице и отображаем их как кнопку с текстом user.nickname (будем отображать их никнеймы, да). К этой же кнопке потом привяжем ссылки на статьи конкретного автора.

Перебор найденных авторов статей

Теперь это добро нужно добавить на панель навигации.

Кнопка для перехода на страницу с авторами

Открываем файл layout.svelte и добавляем туда еще одну кнопку со ссылкой на файл authors.svelte. Готово. 

Прикрепляем ссылки на авторов к статьям

Имя автора должно отображаться на странице slug.svelte. То есть после того, как мы откроем пост и посмотрим его полностью. Поэтому и соответствующую логику будем добавлять в компонент slug

Сначала сделаем функцию getAuthorName. Она как раз и будет отвечать за весь процесс добычи никнейма автора и его отображения под названием статьи. Вот как она выглядит:

Метод для загрузки данных об авторах постов

• Cоздаем сам метод – const getAuthorName = async ( ) => 

• В тело забрасываем стандартный запрос к Supabase: 

let data, error = await supabase.from(‘Users’).select(‘*’).eq(‘userId’, post.userId)

Так мы сможем вытащить сразу все записи из Users, у которых id совпадает с id поста. В нашем случае это будет только одна запись. 

• Закрепляем за переменной author, которую нужно объявить до вызова функции, никнейм автора – author = data[0].nickname. Снова лезем в data по индексу, так как БД возвращает нам массив. 

Функцию getAuthorName можно отправить в getData, чтобы объединить логику получения информации в один блок кода. 

Что касается интерфейса, то достаточно добавить строку <p>{author}</p> под названием статьи. 

Настраиваем страницу для фильтра статей по автору

Сам по себе список авторов не особо полезен, поэтому нам нужна возможность нажать на имя и получить весь список статей, написанных одним человеком. Для этого сделаем еще один аналог slug.svelte в директории authors. Назовем его authorsArticles.svelte. 

Функция отображения постов, отфильтрованных по авторам

Внутри добавим функцию, в которой будет заключена следующая логика:

• Мы импортируем туда базу данных – import supabase from ‘$lib/db’

• Туда же импортируем компонент Postimport Post from ‘../Post.svelte’. Напоминаю, что Post используется для отрисовки отдельных постов. 

• Добавляем в компонент onMountimport onMount from ‘svelte’

• Импортируем объект pageimport page from ‘$app/stores’

• Здесь же создаем переменную id, в которую запишем параметры страницы, передаваемые извне – const id = $page.params.authorsArticles

• Создаем переменную для хранения постов. По умолчанию это будет пустой массив. 

• Теперь описываем асинхронную функцию внутри onMount. В ней будет следующий запрос к базе данных:

let data, error = await supabase.from(‘Posts’).select(‘’).eq(‘userId’, id)

Так мы не только получим все посты, а еще и отфильтруем их по id автора. Но здесь можно использовать и никнейм, если хотите. id просто для примера.

• Обновляем значение posts на dataposts = data

Интерфейс рендеринга должен выглядеть так же, как выглядит рендеринг в компоненте index.svelte. То есть простой перебор постов через блок #each и их отправка в компонент Post с помощью spread-аргументов.

Интерфейс для отрисовки постов

Комьюнити теперь в Телеграм
Подпишитесь и будьте в курсе последних IT-новостей
Подписаться

Теги

Одна из наиболее полезных функций в нашем SvelteKit-приложении. Создадим возможность отсеивать статьи по темам, заранее прописанным со стороны бэкенд-составляющей сайта. 

Создаем список тегов

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

Начнем мы с того, что создадим в интерфейсе Supabase новую таблицу. 

• Открываем в боковой панели пункт управления данными. Панель управления Supabase

• Нажимаем на кнопку создания новой таблицы. 

• Даем ей имя Tags. 

• В списке строк оставляем только id и tagName с форматом text. 

Сохраняем получившуюся таблицу. 

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

Интерфейс добавления записей в таблицу в Supabase

Я указал на скриншоте решетки, потому что так принято, но сам их не использую, потому что они мешают переадресации с одной страницы на другую. 

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

Настраиваем интерфейс для добавления тегов

Есть разные решения для прописывания тегов. Мы не будем искать изыски и выберем самый простой вариант – банальный список кнопок, нажатие на которые добавляет теги к статьям. Там не будет поиска или шибко симпатичного интерфейса. Но все работает, и работает достаточно хорошо. А если потребуется, то дополнительную функциональность реализуем позже.

Открываем файл NewPost.svelte. В верхнюю часть добавляем сразу три новых переменных:

let tags – нужна, чтобы хранить в ней теги. По умолчанию оставляем там пустой массив, чтобы приложение не сыпало ошибками при первичной загрузке или при отсутствии тегов у поста. 

let tagsSelected – здесь будут лежать те теги, что мы выбрали на этапе создания поста. Они же будут отправляться в базу данных (в блок Tags конкретного поста). 

let tagsFocused – эта переменная отвечает за отображение блока с тегами. По умолчанию установлено значение false.

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

Функция поиска тегов в базе данных

• Объявляем сам метод – const getTags = async ( ) => 

• В тело getTags отправляем соотвествующий запрос в БД: 

let data, error = await supabase.from(‘Tags’).select(‘*’)

Никакой фильтрации не нужно, берем сразу все теги. Их все равно не будет слишком много.

• Переназначаем переменную – tags = data

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

Функция toggleInputVisibility

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

Интерфейс подключения новых тегов к статьям

• Начинается все с общего блока, в котором будет функция для переключения режимов отображения списка тегов:

on:click=() => {tagsFocused = ! tagsFocused}

• Создадим div, в который поместим код tagsSelected, чтобы в нем подряд отображались все выбранные теги.

• Добавим в блок с редактором код {#if} tagsFocused {/if}

• Внутрь логического выражения вставляем отдельный div, скрывающий список тегов, но намекающий на их существование. 

• Еще ниже разместим блок {#each} tags as tag {/each}

• В него мы запишем кнопку для добавления тега в список tagsSelected

on:click=(e) => {tagsSelected.push(e.target.innerHTML)}

Туда же стоит добавить переназначение переменной tagsSelected на саму себя, чтобы отобразить в списке тегов актуальную информацию. 

• В качестве innerHTML будем использовать tag.tagName.

Теперь в интерфейсе для добавления статей будет отображаться блок с выбранными тегами. Нажав на него, вы получите доступ ко всем тегам из БД. Клик по каждому будет закреплять его за статьей.

Чтобы в итоге информация оказалась в БД, надо дополнить функцию addPost так, чтобы в ней появилось поле tags, в которое и будет помещаться значение переменной tags, созданной нами ранее.

Прикрепляем теги к блоку со статьей

У нас уже отображается никнейм автора под названием поста. Почему бы туда не отправить еще и список использованных тегов с возможностью сразу перескочить на интересующую тему и посмотреть похожие материалы? Звучит удобно, так что сделаем. Тем более опыт уже есть – мы быстро реализуем и нужную функцию, и интерфейс для нее. 

Фактически будем копировать то же, что делали в случае с именами авторов.

Функция getData, адаптированная под работу с тегами

• Сначала создаем переменную tags в файле [slug].svelte. В нее помещаем пустой массив, чтобы сайт не рухнул при отсутствии тегов для какой-либо из статей. 

• Модифицируем функцию getData, чтобы массив tags дополнялся данными из объекта post.tags, прописав: tags = post.tags

• После этого добавляем простой #each перебора тегов и отображаем его под именем автора. К каждому можно прикрепить ссылку вида <a href=/tags/${tag}></a>. Это будет ссылка, ведущая на файл [tagFilters].svelte в папке tags, который мы создадим чуть позже. 

Настраиваем страницы для фильтра статей по тегу

Нам нужна страница с примененным фильтром в духе той же, что используется в списке статей конкретных авторов. 

• Теперь создаем папку tags, а внутри нее файл [tagFilters].svelte.

Внутрь записываем следующий код:

Содержимое файла tagFilters

• Импортируем Supabase – import supabase from ‘$lib/db’

• Сюда же добавляем Post import Post from ‘../Post.svelte’

• Таким же образом добавляем функции onMount и page.

• Создаем константу tag и приравниваем ее к параметру, получаемому извне – const tag = $page.params.tagFilters

• Создаем переменную для отфильтрованных постов с пустым массивом внутри – let posts = []

• В onMount отправляем следующий код:

let data, error = await supabase.from(‘Posts’).select(‘*’).contains(‘tags’, tag)

Здесь мы впервые используем сложный фильтр contains. Он позволяет нам анализировать целые массивы и проверять, нет ли в них запрашиваемой информации. Это необходимо, чтобы мы могли проверить, подходит ли статья по тегу, при этом анализируя совпадение только с одним из тегов, а не с несколькими сразу. А еще в onMount надо добавить переназначение переменной posts – posts = data

Что касается интерфейса, то можно скопировать тот же код, что используется в [authorsArticles].svelte.

Пагинация

Пагинация – это метод деления страницы на части. Логика заключается в том, чтобы отображать на экране только часть контента и не загружать сразу все материалы, коих может быть великое множество. 

Если вы видите в каком-нибудь блоге цифры под списком статей, то вы столкнулись с пагинацией. Благодаря ей можно быстро перемещаться между материалами. 

Сделаем такой же механизм на своем сайте, причем без сторонних библиотек. Реализуем простейший вариант пагинации:

Переменная range c массивом

• Откроем index.svelte и создадим в нем переменную range с массивом цифр – let range = [0, 4]

Функция обновления списка статей

• Затем мы добавим в функцию getData аргумент range. Он будет использоваться для ограничения подачи контента прямо из базы данных. Для этого мы перемешаем алгоритм метода, дополнив его новой директивой:

let data, error = await supabase.from(‘Posts’).select(‘’).range(range[0], range[1])

Мы используем массив с цифрами для ограничения количества постов при первичной загрузке. 

Функция загрузки постов с ограничениями

• Теперь нам нужна функция для изменения значения range. Назовем ее updateData.

Мы будем проверять передаваемый аргумент. Если этот аргумент ‘next’, то будем увеличивать значения обоих чисел в range. Если же аргументом будет ‘back’, то мы будем уменьшать значения range. После изменения показателей range снова запустим getData(range), чтобы обновить информацию на странице. 

А ниже нарисуем такой несложный интерфейс:

Интерфейс пагинации

Одна кнопка будет запускать функцию updateData с аргументом ‘next’. А другая – с аргументом ‘back’. Тут важно их ограничить, сделав так, чтобы нужные кнопки отображались в нужное время. Например, в кнопке «Назад» нет смысла, если значение range[0] меньше 0, так как это означает, что мы на первой странице. То же касается кнопки «Вперед». Если длина массива posts не равна длине промежутка между значениями в массиве range, то эту кнопку можно блокировать, так как она не будет работать должным образом. 

Markdown

С поддержкой markdown все куда сложнее. Простого варианта реализовать полноценную конвертацию символов не существует. Поэтому мы впервые воспользуемся сторонней библиотекой для Svelte. Благо на просторах npm есть легковесная и простая в использовании библиотека svelte-markdown. 

Чтобы ее установить, открываем терминал в директории с нашим проектом и пишем:

 npm install svelte-markdown

По завершении установки надо вернуться к коду и модифицировать пару функций. 

• Открываем файл slug.svelte.

• Импортируем в него SvelteMarkdown: 

import MarkdownSvelte from ‘svelte-markdown’

• Потом добавляем компонент SvelteMarkdown на место post.body, а post.body передаем в качестве пропса – <SvelteMarkdown source=post.body />

Теперь вы можете добавлять в редакторе символы, присущие Markdown, и они будут отображаться согласно правилам разметки. 

Это хорошо видно на скриншотах. 

Запись в SvelteKit-блоге

Добавляем звездочки, чтобы выделить слово population жирным.

Редактор статей в SvelteKit-блоге

И вот оно.

Markdown-разметка

Вместо заключения

Как видите, блог почти готов. Мы добавили кучу важных функций и полезных опций. Дело осталось за малым. Надо решить вопрос с изображениями, реализовать подобие лайков и добавить на сайт поддержку профилей (можно будет вести базовую статистику, такую как количество статей автора, число лайков и т.п.). Возможно, прикрутим комментарии, Supabase это позволяет. А на завершающем этапе проведем оптимизацию кода, удалим мусор и проведем редизайн. 

Пока все. Если будут вопросы, постараюсь помочь в комментариях. Спасибо за внимание!

Продолжение: Блог на Svelte. Часть 7: Загрузка картинок в оглавление статьи

Hello World! Гайды и обзоры для девелоперов разных мастей.

Комментарии

С помощью соцсетей
У меня нет аккаунта Зарегистрироваться
С помощью соцсетей
У меня уже есть аккаунт Войти
Инструкции по восстановлению пароля высланы на Ваш адрес электронной почты.
Пожалуйста, укажите email вашего аккаунта
Ваш баланс 10 ТК
1 ТК = 1 ₽
О том, как заработать и потратить Таймкарму, читайте в этой статье
Чтобы потратить Таймкарму, зарегистрируйтесь на нашем сайте