Админка магазина на vue.js. Урок 1. Список товаров
С админкой на файлах мы закончили, пора переходить к вещам посерьезнее. На очереди админка для интернет-магазина, уже на базах данных, все как положено. За магазином далеко ходить не нужно, возьмем наш shop.webdevkin.ru. Будет здорово, если Вы с ним уже знакомы. Если нет, то посмотрите статью Структура базы данных в интернет-магазине, для админки этого хватит. К тому же по ходу уроков я буду стараться пояснять неочевидные моменты и давать ссылки на нужные места магазина. Постараемся не заблудиться :-)
В админке на файлах мы сделали минимальный набор функций: список редактируемых параметров и все. Но интернет-магазин дело посложнее. Мы хотим уметь работать с товарами, категориями, ценами и брендами. Поэтому одной статьей не обойдемся, будет серия уроков. Уроки небольшие, чтобы освоить материал за один раз и при этом не скучать.
Важный момент: админку будем писать на Vue.js. Почему? До этого клиентский код мы писали на смеси javascript + jquery. Но во-первых, одно и то же делать скучно. Во-вторых, админка - проект сложнее, чем внешняя часть магазина, например, корзина или даже фильтры. Поэтому лучше писать на специальных фреймворках или библиотеках. В-третьих, новые знания всегда пригодятся.
Почему vue? Мне не нравится ангуляр. Он выглядит чересчур сложным и местами заумным, а я за простые решения. На реакте пишут многие, и в интернетах по нему до фига туториалов. Я не гнушаюсь и бекбоном, но 2018 год, рассказыть о нем будет странно. В общем, из всего этого разнообразия мне больше нравится vue. У него похожий на реакт подход (только проще, на мой взгляд), отличная документация, растет популярность и звездочки на гитхабе. Насчет документации убедился сам: чтобы начать эту админку, мне хватило полдня для изучения основ vue. Чтобы понять код из уроков, Вам будет достаточно пробежать глазами раздел Основы. Попробуйте.
Еще важный момент: vue я только начал изучать. Поэтому пробовать, ошибаться и учиться дальше будем вместе. Начнем с самого простого, потом постепенно усложним, попробуем разделить приложение на компоненты, изучим взаимодействие с сервером, потрогаем vuex и роутинг. Только все это не на todo-списках, а на интернет-магазине. Если Вы знаете vue, круто, если будете делиться хорошими идеями и замечаниями в комментах.
Описания основ vue в статьях не будет. Это займет много времени. И я не объясню основы лучше документации, которая даже в переводе написана на удивление по-человечески.
Давайте определимся, что хотим от первого урока. Предлагаю подключить vue и вывести список товаров. Серверной части сегодня не будет, воспользуемся тем, что мы делали в фильтрах для интернет-магазина. https://shop.webdevkin.ru/scripts/catalog.php - этот запрос вернет список товаров. Откройте в браузере и убедитесь, что запрос работает. Возвращает объект { code: 'success', data: { goods: [/* массив товаров */] }} Чтобы начать разрабатывать локально, скачайте исходники магазина и накатите sql-миграции. Саму админку будем делать в папке admin.
Заготовка проекта и html-код.
Создадим в папке admin 3 файла: index.html, app.js и style.css. В index.html подключим два css файла: https://cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.1/mini-default.css и ./style.css. Это еще одно нововведение: верстать будем с помощью библиотечки mini.css. На бутстрапе у нас сделан сам магазин да и много других примеров, давайте попробуем что-то другое.
Теперь скрипты. Подключаем в конце index.html 3 скрипта: https://cdn.jsdelivr.net/npm/vue/dist/vue.js, https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js и ./app.js. Сам vue, axios - библиотека для ajax-запросов и наш собственный файлик.
Пора написать первый код в index.html.
Здесь мы видим обычную верстку. id="app" корневому контейнеру задаем, потому что vue хочет знать, откуда начинаются ее полномочия. header и footer с гордыми логотипом "Webdevkin". И сетка с заголовком страницы h1 и пока что комментом "Список товаров" вместо реальных данных. Про сетку, header и footer можно почитать в документации mini.css.
Пока что ничего нового. Новое будет в следующем коде на месте списка товаров
Id | Название | Бренд | Цена | Рейтинг |
---|---|---|---|---|
Обычная таблица с необычными параметрами в tr. Расскажу по очереди.
is="ProductItem" означает, что за одну запись в таблице формирует компонент ProductItem. Его напишем в app.js, потерпите.
v-for="product in products" - это цикл. У нас есть некая переменная products, хранящая список всех товаров. Мы перебираем все товары и выдергиваем их по одному в переменную product.
Следующая же строка :product="product" говорит, что эта переменная передается в компонент ProductItem под именем product. Чтобы понять, где какой "product", можно эти строчки переписать так
v-for="item in filteredProducts" :product="item" :key="item.good_id"
Последняя :key="product.good_id" пока нам не понадобится, но документация vue рекомендует всегда передавать параметр key из цикла в компонент. Зачем-то это надо, придет время, выясним.
С html закончили. Сначала непривычно, что в разметку добавляются такие странные атрибуты. Но постепенно мы будем понимать, что это удобно. Глядя на хтмл, уже можно представить, какой код нас ждет в javascript. Давайте его напишем.
app.js - javascript-основа приложения
Заготовка выглядит так
// Экземпляр vue var app = new Vue({ el: '#app', data: { products: [] }, mounted: function() { var that = this; axios .get('/scripts/catalog.php') .then(function(response) { that.products = response.data.data.goods; }); } });
new Vue({ ... }) - это создание экземпляра vue. Дальше вся сила в параметрах.
el: '#app' показывает, где находится корневой элемент для vue.
В объекте data храним все данные, доступные в приложении. products - массив товаров, по умолчанию пустой.
mounted - это функция, которая срабатывает после монтирования экземпляра vue.
В mounted нам нужно получить товары с сервера, делаем это с помощью axios. Методом get дергаем /scripts/catalog.php и в колбеке then копируем в products полученный с сервера массив.
response.data.data.goods - 2 раза data, почему? В объекте response от axios хранятся не только сами данные от сервака, но еще http-код ответа, статус и некоторые другие вещи.
А в data собственно данные. А второй data - это мы следуем по цепочке объекта, вернувшегося с сервера.
Напоминаю, ответ с сервера смотрите здесь - https://shop.webdevkin.ru/scripts/catalog.php
Дополнительных параметров вроде content-type указывать не нужно - axios сам понимает, если вернулся json, то сразу его и парсит.
Теперь нам нужно создать компонент ProductItem, который отвечает за один товар. Напишем в начало app.js такой код
// Компонент продукта Vue.component('ProductItem', { props: ['product'], template: `` }); {{ product.good_id }} {{ product.good }} {{ product.brand }} {{ product.price }} {{ product.rating }}
Здесь мы создаем компонент с таким-то названием и таким-то шаблоном. В массиве props перечисляем все свойства, которые передаются в компонент из родительского. Помните, оттуда, где мы в html писали v-for="product in products" и :product="product". А шаблон это обычная строка с плейсхолдерами. Они указываются в двойных фигурных скобках.
Вот теперь можно обновить страницу и посмотреть на получившуюся таблицу.
Немного удиляет дефолтная таблица mini.css, которая по умолчанию 400px высотой. Мне это не понравилось, поэтому перезатер нужные стили в файле style.css
table:not(.horizontal) { max-height: 1000px; }
Вот так у нас получилось. Я бы на этом закончил статью, но не удержусь и продемонстрирую, как реализовать простой поиск товаров. На чистом js или jquery нам пришлось бы сохранять где-то данные, навешивать события на изменения инпута поиска и перерисовывать таблицу. На vue происходит то же самое, только многое без нашего участия.
Поиск по названию товара.
Добавим в index.html перед таблицей поле поиска
v-model означает, что в приложении помимо массива products появляется еще один параметр inputSearch. Модификатор .trim будет обрезать пробелы с обеих сторон инпута.
И еще одно. Заменим строку
v-for="product in products" на v-for="product in filteredProducts".
filteredProducts - это еще одно поле, которое будет отдавать массив товаров с учетом поля поиска.
Переходим в app.js. Сначала в объект data добавим новое поле inputSearch: ''. А затем после data напишем еще один блок
computed: { filteredProducts: function() { var that = this; return this.products.filter(function(product) { return product.good.toLowerCase().indexOf(that.inputSearch.toLowerCase()) !== -1; }); } }
Что такое объект computed? В нем хранятся вычисляемые поля - это такие, значения которых зависят от других полей. Наш filteredProducts зависит от product и inputSearch одновременно. В filteredProducts возвращаем массив, отфильтрованный по принципу совпадения инпута с названиями товаров. К нижнему регистру приводим для удобства, чтобы не заставлять себя в нужные моменты нажимать shift.
Весь кайф вычисляемых полей в том, что не нужно думать, когда меняется inputSearch или products - при их изменении зависимые поля вычислятся сами. Или точнее при обращении к ним вызывается функция, которая рассчитывает новое значение. Но не суть, это не так важно. Важно то, что больше ничего для поиска делать не нужно. Навешивание событий, их прослушка и рендер таблицы - все это делает vue за нас. Мы же больше сосредотачиваемся на логике приложения, а не на всяких скучных вещах.
На этом первый урок по админке на vue закончен. Демо смотрите по этой ссылке, а здесь можно скачать исходники.
Скоро будут новые уроки. До встречи!
Все уроки админки на vue.js
- Урок 1. Список товаров
- Урок 2. Фильтры и сортировки
- Урок 3. Новое REST API на чистом PHP
- Урок 4. Правим клиентский код под новое REST API и находим багу
- Урок 5. Разбиваем приложение на компоненты
- Урок 6. Инструмент vue-cli и vue-компоненты
- Урок 7. Flux и Vuex - общие вопросы
- Урок 8. Vuex на практике
- Урок 9. Перерабатываем фильтры
- Урок 10. Добавляем и удаляем бренды
- Урок 11. Обрабатываем ошибки на клиенте и сервере
- Урок 12. Редактируем бренды
- Урок 13. Роутинг
- Урок 14. Карточка товара
Что еще почитать по теме
- Корзина интернет-магазина
- Фильтры и сортировки в магазине
- Структура базы данных в магазине
- Админка на файлах. Серия уроков
Истории из жизни айти и обсуждение кода.