Назад | К содержанию | Дальше
Внимание! Код, который мы пишем всё ещё "плохой" и пишем мы его только в ознакомительных целях, чтобы понимать что такое хорошо и что такое плохо.
31 минута
Содержание урока:
Материалы к изучению:
Проблема: при обновлении страницы у нас пропадает список тикеров
В методе add() после добавления нового тикера в массив тикеров сохраняем его в localStorage
LocalStorage это хранилище данных в браузере. Данные хранятся парами "ключ:значение", причем значения могут хранить только скалярные типы данных. Поэтому перед записью данных мы их "сериализуем" (преобразуем в строку) методом JSON.stringify, а после извлечения "десериализуем" методом JSON.parse
localStorage.setItem(
'cryptonomicon-list',
JSON.stringify(tickers.value))
Восстанавливаем список тикеров в методе жизненного цикла
В видео используется метод created, но во vue3 нет такого метода, будем использовать onBeforeMount (в видео запретили использовать метод onMount без объяснения причин)
onBeforeMount(() => {
const tickersData = localStorage.getItem('cryptonomicon-list') ?? '[]'
tickers.value = JSON.parse(tickersData)
})
Если сейчас обновить страницу, то тикеры восстановятся, но обновления данных не будет, т.к. оно включается только в методе add
Выносим код обновления тикеров в отдельную функцию subdcribeToUpdates
function subscribeToUpdates (tickerName) {
setInterval(async () => {
const f = await fetch(
`https://min-api.cryptocompare.com/data/price?fsym=${tickerName}&tsyms=USD&api_key=ce3fd966e7a1d10d65f907b20bf000552158fd3ed1bd614110baa0ac6cb57a7e`
)
const data = await f.json()
// тут я добавил проверку data.USD - некоторые валюты возвращают ошибку
if (typeof data.USD != 'undefined') {
tickers.value.find(t => t.name === tickerName).price = data.USD > 1 ? data.USD.toFixed(2) : data.USD.toPrecision(2)
if (sel.value?.name === tickerName) {
graph.value.push(data.USD)
}
}
}, 5000);
}
И используем её и в методе add (сделайте сами) и в onBeforeMount
onBeforeMount(() => {
const tickersData = localStorage.getItem('cryptonomicon-list') ?? '[]'
tickers.value = JSON.parse(tickersData)
tickers.value.forEach(ticker => {
subscribeToUpdates(ticker.name)
})
})
В шаблоне перед списком тикеров добавим фильтр и кнопки
<div>
Фильтр: <input/>
<button
class="my-4 mx-2 inline-flex items-center py-2 px-4 border border-transparent shadow-sm text-sm leading-4 font-medium rounded-full text-white bg-gray-600 hover:bg-gray-700 transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
>Назад</button>
<button
class="my-4 mx-2 inline-flex items-center py-2 px-4 border border-transparent shadow-sm text-sm leading-4 font-medium rounded-full text-white bg-gray-600 hover:bg-gray-700 transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
>Вперед</button>
</div>
В скрипте определяем реактивные переменные
const page = ref(1)
const filter = ref('')
В шаблоне в поле ввода вильтра добавляем двустороннее связывание с нашей реактивной переменной
Фильтр: <input v-model="filter"/>
В скрипте создаём функцию, возвращающую фильтрованный список тикеров:
function filteredTickers () {
return tickers.value.filter(t => t.name.includes(filter.value.toUpperCase()))
}
И вставляем вызов этого метода в шаблон вместо tickers (сделайте самостоятельно)
При добавлении нового тикера сбрасываем фильтр (тоже сделайте самостоятельно)
С потолка взяли размер страницы в 6 тикеров.
Для получения части списка дописываем метод filteredTickers
const start = (page.value - 1) * 6
const end = page.value * 6
return tickers.value
.filter(t => t.name.includes(filter.value.toUpperCase()))
.slice(start, end)
Добавим логику кнопкам вперед и назад
Условия видимости и обработчики клика можно взять из видео. А реализация filteredTickers меняется для поддержки hasNextPage
const start = (page.value - 1) * 6
const end = page.value * 6
const tempFilteredTickers = tickers.value.filter(t => t.name.includes(filter.value.toUpperCase()))
hasNextPage.value = tempFilteredTickers.length > end
return tempFilteredTickers.slice(start, end)
При изменении фильтра, если мы находимся на 2-й странице, то данных для отображения может не оказаться. Для решения этой проблемы решили сбрасывать пагинацию при изменении фильтра.
Как это сделать? Во vue для определения изменений в реактивных переменных есть наблюдатели. Наблюдатели, как и обработчики хуков жизненного цикла, пишем прямо в коде script setup
Синтаксис простой:
watch(переменная, (newValue, oldValue) => {
код, срабатывающий при изменении переменной
})
Параметры newValue и oldValue можно не указывать, если они не нужны
В итоге получается такой наблюдатель:
watch(filter, () => {
page.value = 1
})
При изменении фильтра добавляем в путь параметр filter:
watch(filter, () => {
page.value = 1
window.history.pushState(
null,
document.title,
`${window.location.pathname}?filter=${filter.value} `
)
})
Наблюдатель для page (с сохранением истории) реализуйте самостоятельно
Теорию посмотрите в видео, я сразу привожу готовый код (напоминаю, что метода created у нас нет, пишем прямо в script setup)
const windowData = Object.fromEntries(
new URL(window.location).searchParams.entries()
)
page.value = windowData.page ?? 1
filter.value = windowData.filter ?? ''
Назад | К содержанию | Дальше