Shop admin on vue.js

 We have finished with the admin panel on files, it's time to move on to more serious things. Next in line is the admin panel for the online store, already on the databases, and everything is as it should be. You don't need to go far for the store, let's take our shop.webdevkin.ru . It would be great if you already know him. If not, then look at the article Database structure in an online store, this is enough for the admin panel. In addition, in the course of the lessons, I will try to explain non-obvious points and give links to the right places in the store. Let's try not to get lost :-)

In the admin panel on files, we made a minimal set of functions: a list of editable parameters and that's it. But an online store is more complicated. We want to be able to work with products, categories, prices, and brands. Therefore, one article will not do, there will be a series of lessons. The lessons are small so that you can master the material at a time and not get bored at the same time.

An important point: we will write the admin panel on Vue.js. Why? Before this, we wrote client code using a mixture of javascript + jquery. But firstly, doing the same thing is boring. Secondly, the admin panel - the project is more complicated than the external part of the store, for example, a shopping cart or even filters. Therefore, it is better to write on special frameworks or libraries. Thirdly, new knowledge is always useful.

Why Vue? I don't like Angular. It looks too complex and sometimes abstruse, and I am for simple solutions. Many people write on react, and on the Internet, there are a lot of tutorials on it. I do not disdain backbone, but in 2018, it would be strange to talk about it. In general, of all this variety, I like vue. It has a React-like approach (only simpler, in my opinion), excellent documentation, growing popularity, and stars on Github. As for the documentation, I have convinced myself: that to start this admin panel, it took me half a day to learn the basics of vue. To understand the code from the lessons, it will be enough for you to skim the Basics section with your eyesTry it.

Another important point: I just started learning vue. Therefore, we will try, make mistakes and learn further together. Let's start with the simplest, then gradually complicate it, try to divide the application into components, study the interaction with the server, touch vuex, and routing. Only all of this is not on to-do lists but on an online store. If you know vue, it's cool if you share good ideas and comments in the comments.

There will be no description of the basics of vue in the articles. It will take a lot of time. And I will not explain the basics better than the documentation, which, even in translation, is written in a surprisingly human way.

Let's decide what we want from the first lesson. I propose to connect vue and display a list of products. There will be no server part today, we will use what we did in the filters for the online store. https://shop.webdevkin.ru/scripts/catalog.php - this request will return a list of products. Open in a browser and check that the request works. Returns object { code: 'success', data: { goods: [/* array of goods */] }} To start developing locally, download store sources and run SQL migrations. We will do the admin panel itself in the admin folder.


Project blank and HTML code.

Let's create 3 files in the admin folder: index.html, app.js, and style.css. Let's include two CSS files in index.html: https://cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.1/mini-default.css and ./style.CSS. This is another innovation: we'll use the mini.css library to typeset it. On the bootstrap, we have made the store itself and many other examples, let's try something else.

Now scripts. We include 3 scripts at the end of index.html: https://cdn.jsdelivr.net/npm/vue/dist/vue.js, https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios .js and ./app.js. Vue itself, Axios - a library for ajax requests and our own file.

It's time to write the first code in index.html.

one
2
3
4
5
6
7
eight
nine
ten
eleven
12
thirteen
fourteen
fifteen
sixteen
17
eighteen
nineteen
<div id="app">
    <div class="container">
        <header class="sticky">
            <a href="/" class="logo">Webdevkin</a>
            <a href="./" class="button">Каталог</a>
        </header>
        <div class="row">
            <div class="col-sm-1"></div>
            <div class="col-sm-10">
                <h1>Список товаров</h1>
                <!-- Список товаров -->
            </div>
            <div class="col-sm-1"></div>
        </div>
        <footer>
            <p>Разработка <a href="https://webdevkin.ru" target="_blank">Webdevkin</a></p>
        </footer>
    </div>
</div>

Here we see the usual layout. We set id="app" to the root container because vue wants to know where its permissions come from. header and footer with a proud "Webdevkin" logo. And a grid with the page title h1 and for now the comment "List of products" instead of real data. You can read about the grid, header, and footer in the mini.css documentation.

So far, nothing new. The new one will be in the following code in place of the product list

one
2
3
4
5
6
7
eight
nine
ten
eleven
12
thirteen
fourteen
<table>
    <thead>
        <tr>
            <th>Id</th>
            <th>Название</th>
            <th>Бренд</th>
            <th>Цена</th>
            <th>Рейтинг</th>
        </tr>
    </thead>
    <tbody>
        <tr is="ProductItem" v-for="product in products" :product="product" :key="product.good_id"></tr>
    </tbody>
</table>

Ordinary table with unusual parameters in tr. I'll tell you in turn.

is="ProductItem" means that one record in the table generates the ProductItem component. We will write it in app.js, be patient.
v-for="product in products" is a loop. We have a product variable that holds a list of all products. We loop through all the products and pull them out one by one into the product variable.
The next line:product=" product" says that this variable is passed to the ProductItem component under the name product. To understand where what "product" is, you can rewrite these lines like this

one
2
3
v-for="item in filteredProducts"
:product="item"
:key="item.good_id"

We won't need the last:key=" product.good_id" for now, but the vue documentation recommends always passing the key parameter from the loop to the component. Why is this necessary, the time will come, and we will find out.

Finished with HTML. At first, it is unusual that such strange attributes are added to the markup. But gradually we will understand that it is convenient. Looking at the HTML, you can already imagine what kind of code awaits us in javascript. Let's write it.


app.js - javascript-based application

The workpiece looks like this

one
2
3
4
5
6
7
eight
nine
ten
eleven
12
thirteen
fourteen
fifteen
// Экземпляр 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({ ... }) is the instantiation of view. Then all the power is in the parameters.
el: '#app' shows where the root element for vue.
In the data object, we store all the data available in the application. products - an array of products, empty by default.
mounted is a function that fires after the Vue instance is mounted.
In the mounted we need to get the goods from the server, we do this with the help of Axios. Using the get method, we pull /scripts/catalog.php and in the then callback we copy the array received from the server into products.
response.data.data.goods - 2 times data, why? The response object from Axios stores not only the data from the server itself, but also the response HTTP code, status, and some other things. And in data is the actual data. And the second data is we follow the chain of the object returned from the server. I remind you, see the response from the server here - https://shop.webdevkin.ru/scripts/catalog.php
You do not need to specify additional parameters like content-type - Axios itself understands, if JSON is returned, then it is immediately parsed.

Now we need to create a ProductItem component that is responsible for one product. Let's write the following code at the beginning of app.js

one
2
3
4
5
6
7
eight
nine
ten
eleven
12
thirteen
// Компонент продукта
Vue.component('ProductItem', {
    props: ['product'],
    template: `
     
        {{ product.good_id }}
        {{ product.good }}
        {{ product.brand }}
        {{ product.price }}
        {{ product.rating }}
     
    `
});

Here we create a component with such and such a name and such and such a template. In the props array, we list all the properties that are passed to the component from the parent. Remember, from where we wrote v-for="product in products" and :product="product" in HTML. A template is a regular string with placeholders. They are enclosed in double curly braces.

Now you can refresh the page and look at the resulting table.

A bit different is the default mini.css table, which is 400px high by default. I didn't like it, so I have overwritten the necessary styles in the style.css file

one
2
3
table:not(.horizontal) {
    max-height: 1000px;
}

This is how we did it. I would end the article on this, but I can’t resist and demonstrate how to implement a simple product search. On pure js or jquery, we would have to save data somewhere, hook events to changes in the search input, and redraw the table. On vue, the same thing happens, only a lot without our participation.


Search by product name.

Add a search field in index.html before the table

one
<input v-model.trim="inputSearch" type="text" placeholder="Поиск по названию товара">

v-model means that in addition to the products array, one more input search parameter appears in the application. The .trim modifier will trim spaces on both sides of the input.

One more thing. Let's replace the line

one
v-for="product in products" на v-for="product in filteredProducts".

filtered products is another field that will return an array of products based on the search field.

Let's go to app.js. First, let's add a new field input search: '' to the data object. And then after data, we will write another block

one
2
3
4
5
6
7
eight
computed: {
    filteredProducts: function() {
        var that = this;
        return this.products.filter(function(product) {
            return product.good.toLowerCase().indexOf(that.inputSearch.toLowerCase()) !== -1;
        });
    }
}

What is a computed object? It stores calculated fields - these are those whose values ​​depend on other fields. Our filtered products depend on product and input search at the same time. In filtered products, we return an array filtered by the principle of matching the input with the names of the products. We bring it to lowercase for convenience, so as not to force yourself to press shift at the right moments.

The whole point of calculated fields is that you do not need to think when input search or products change - when they change, dependent fields will be calculated by themselves. Or rather, when accessing them, a function is called that calculates a new value. But that's not the point, it's not that important. The important thing is that you don't need to do anything else to search. Hanging events, listening to them, and rendering the table - all this is done by vue for us. We focus more on the logic of the application, and not on all sorts of boring things.

Comments