Holy use Tuture combat tutorial writing tools Holy use Tuture combat tutorial writing tools Holy use Tuture combat tutorial writing tools Holy use Tuture combat community, welcome to join the community, create wonderful free technology combat tutorial, to force the development of programming industry.
In the previous six tutorials we have basically implemented the mini full stack e-commerce application, I believe you have a comprehensive understanding of the development of a full stack application. However, an engineer who pursues perfection will not be stingy with his artistic creation. Only realizing the function of the application cannot meet the high demand of users, and the interface effect of the application is also a key factor to improve user experience. Therefore, this tutorial will rebuild the front-end code of the project based on the Element-UI component library to improve the interface of the mini e-commerce application and enhance the user experience. Although we can easily introduce the existing component library, the corresponding data processing is also worth our attention, so I will introduce the component library at the same time with us to step on the element UI pit for us, after all, step on the pit to grow.
Welcome to the Series from Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express:
- From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (PART 1)
- From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (PART 2)
- From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (Part 3)
- From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (4)
- From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (5)
- From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (6)
- From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (7)
- From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (Final)
If you want to start directly from this step, run the following command:
git clone -b section-seven https://github.com/tuture-dev/vue-online-shop-frontend.git
cd vue-online-shop-frontend
Copy the code
The source code for this article is available on Github. If you think we did a good job, please give ❤️ a thumbs up +Github repository + star ❤️
Code refactoring
In this part, we mainly use the Element-UI component library to reconstruct the previous project code to achieve a very aesthetic mini e-commerce application.
Here’s a quick look at the Element-UI component library (you can skip this section if you know) :
Element UI is a Vue 2.0-based component library for developers, designers, and product managers. It provides design resources to help websites take shape quickly.
The Element UI documentation provides a lot of sample code, and we can usually just copy the sample code and have a look at changing the data and so on. But in some scenarios, we may need to use to some special functions and attributes, and these functional properties are generally in the official offer components have been built in, so we can directly from the document first find out and see if any attribute or method can meet our requirements, so as to avoid repetition of rolling.
Install element-UI dependencies
- NPM installation is recommended as NPM, which works better with the WebPack packaging tool.
npm i element-ui -S
Copy the code
- CDN Import You can obtain the latest version of CDN resources from unpkg.com/element-ui. You can import JS and CSS files on the page to use CDN.
<! --><link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> <! -- Import component library --><script SRC ="https://unpkg.com/element-ui/lib/index.js"></script>
Copy the code
We recommend that users who introduce Element using CDN lock the version at the link address to prevent future Element upgrades from being affected by incompatibility updates. See unpkg.com for details on how to lock the version.
Import dependence
After the dependency installation is complete, we need to import and register the dependencies in the project’s main.js file.
You can introduce the whole Element, or just a few components if you want, but here we have introduced the whole Element.
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue';
import { ValidationProvider } from 'vee-validate';
import App from './App';
import router from './router';
import store from './store';
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.config.productionTip = false;
Vue.component('ValidationProvider', ValidationProvider);
Vue.use(ElementUI);
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'});Copy the code
In the main.js file we import the element-UI component library first. Note that we import the style file separately; In addition, the component library is registered with vue.use ().
At this point, a Vue – and Element-based development environment is in place, and you are now happy to use the component library for code refactoring.
Refactoring navigation
Let’s start with the App component, which used to display the homepage navigation with a plain nav TAB. Now we can use the El-Menu navigation component provided by the Element-UI component library to rebuild the navigation bar, which is absolutely cool.
<template>
<div id="app">
<el-menu
class="menu"
:default-active="activeIndex2"
mode="horizontal"
@select="handleSelect"
background-color="#545c64"
text-color="#fff"
active-text-color="#ffd04b">
<el-menu-item index="1"><router-link to="/" tag="div">Home</router-link></el-menu-item>
<el-submenu index="2">
<template slot="title">Admin</template>
<el-menu-item index="2-1"><router-link to="/admin" tag="div">To check the goods</router-link></el-menu-item>
<el-menu-item index="2-2"><router-link to="/admin/new" tag="div">Add the goods</router-link></el-menu-item>
<el-menu-item index="2-3"><router-link to="/admin/manufacturers" tag="div">View the manufacturer</router-link></el-menu-item>
<el-menu-item index="2-4"><router-link to="/admin/manufacturers/new" tag="div">Additive manufacturer</router-link></el-menu-item>
</el-submenu>
<el-menu-item index="3"><router-link to="/cart" tag="div">Cart</router-link></el-menu-item>
</el-menu>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
activeIndex: '1'.activeIndex2: '1'
};
},
methods: {
handleSelect(key, keyPath) {
console.log(key, keyPath); }}};</script>
// ...
Copy the code
I believe you can understand the use of the navigation bar component, but we will only talk about the special areas. We don’t need to worry about the data attribute or the handleSelect method, which we won’t use yet. One special feature here is the tag attribute in the el-Menu-item tag. We set its value to “div” to render the tag as a” div” box. If we do not set this attribute, the tag will be rendered as an “A” tag by default, resulting in the contents of the tag wrapped with an underscore. So the tag attribute is set to remove the underline.
Refactoring the list of items
Modify the ProductList component again. Since the ProductItem child of this component was refactored, some changes are needed here as well. See the ProductItem component refactored below to see what we are doing here.
<template>
<div>
<div class="products">
<div class="container">
This is ProductList
</div>
<! -- <template v-for="product in products"> -->
<product-item :products="products"></product-item>
<! -- </template> -->
</div>
</div>
</template>
<script>
import ProductItem from './ProductItem.vue';
export default {
name: 'product-list',
created() {
if (this.products.length === 0) {
this.$store.dispatch('allProducts')}},computed: {
// a computed getter
products() {
return this.$store.getters.allProducts; }},components: {
'product-item': ProductItem
}
}
</script>
Copy the code
Here, we used v-for to iterate through the products array in each ProductItem component to display the product object separately. However, we cancel v-for to iterate through the Products array. Choose to pass the Products array directly into the ProductItem component. Allow me to keep this in suspense and read on.
Re-enter the ProductItem component for modification, where we use the El-Table component provided by the Element-UI component library instead of the original tag to display the list of items.
<template>
<div class="products">
<el-table
class="table"
:data="products"
max-height="250">
<el-table-column
prop="name"
label="Product Name"
width="180">
</el-table-column>
<el-table-column
prop="description"
label="Introduction"
width="180">
</el-table-column>
<el-table-column
prop="price"
label="Price"
width="180">
</el-table-column>
<el-table-column
prop="manufacturer.name"
label="Manufacturer"
width="180">
</el-table-column>
<! <img: SRC ="image" Alt ="" class="product__image"> </el-table-column> -->
<el-table-column
label="Operation"
width="180">
<template slot-scope="scope">
<product-button :id="scope.row._id"></product-button>
</template>
</el-table-column>
</el-table>
</div>
// ...
</template>
// ...
<script>
import ProductButton from './ProductButton';
export default {
name: 'product-item'.props: ['products'].components: {
'product-button': ProductButton,
}
}
</script>
Copy the code
In fact, I believe that we can understand the modification here, we will simply introduce. You may remember that we sold a mystery above. Why did we pass the Products array directly to this component instead of iterating over the Product object? The el-Table component needs to pass in an array as a data property, and pass in each element object as a prop to display the column name.
In addition, you may have noticed that the prop property is not defined in the last el-table-column tag. This is because the last cell in the el-table-column contains the button instead of the item information. The button is used to specify the action on the specified row object. Here we use scope.row to get the specified row object and pass its ID to the ProductButton button component.
Slot-scope provides access to row, column, $index, and Store (state management within a table) data
Enter the ProductButton component again for modification. Here we use the El-Button component provided by the Element-UI component library to replace the normal button label and modify the corresponding data processing.
<template>
<div>
<el-button
v-if="isAdding"
@click="addToCart"
type="text"
size="small">Add to shopping cart</el-button>
<el-button
v-else
@click="removeFromCart(id)"
type="text"
size="small">Remove from shopping cart</el-button>
</div>
</template>
<script>
export default {
props: ['id'].computed: {
product() {
let product = this.$store.getters.allProducts.find(product= > product._id === this.id)
return product;
},
isAdding() {
let isAdding = true;
this.cart.map(product= > {
if (product._id === this.product._id) {
isAdding = false; }});return isAdding;
},
cart() {
return this.$store.state.cart; }},methods: {
addToCart() {
this.$store.commit('ADD_TO_CART', {
product: this.product,
})
},
removeFromCart(productId) {
this.$store.commit('REMOVE_FROM_CART', {
productId,
})
}
}
}
</script>
Copy the code
Here we simply use the el-Button button component first, and then change the product object obtained from the parent component to an ID. Since we pass in the ProductItem component the id of the specified object, we define the computed property product in the button component. Gets the product object with the specified ID locally.
We can’t wait to get the project off the ground, and look at how our home navigation and product listings have changed incredibly:
Refactoring the product information function
This part is mainly about the reconstruction of commodity information functions, including the display of commodity information list, modification of specified commodity information and addition of new commodities. We have used the components provided by the Element-UI component library for reconstruction, so as to improve the interactive experience of users when operating commodity information.
First, we enter the Products component. We also use the El-Table component provided by the Element-UI component library to replace the normal table to display the list of product information.
<template>
<div class="products">
<el-table
class="table"
:data="products">
<el-table-column
prop="name"
label="Name"
width="180">
</el-table-column>
<el-table-column
prop="price"
label="Price"
width="180">
</el-table-column>
<el-table-column
prop="manufacturer.name"
label="Manufacturer"
width="180">
</el-table-column>
<el-table-column
label="Operation"
width="200">
<template slot-scope="scope">
<el-button class="modify" type="text" size="small"><router-link :to="'/admin/edit/' + scope.row._id">Modify the</router-link></el-button>
<el-button class="remove" @click="removeProduct(scope.row._id), deleteRow(scope.$index, products)" type="text" size="small">delete</el-button>
</template>
</el-table-column>
</el-table>
// ...
</div>
</template>
// ...
Copy the code
Yes, the table here is very similar to the table in ProductItem. It is used to display local product information, but the difference is the operation on the product object. The button component in the ProductItem component is used to add or remove items from the cart, and the button component in the ProductItem component is used to modify or delete item objects.
Here is our reconstructed product information list:
Then we refactor the modification function and go to the Edit component again, where we make data processing changes to try to solve the problem that the product information form cannot be edited.
<template>
<div>
<div class="title">
<h1>This is Admin/Edit</h1>
</div>
<product-form
@save-product="updateProduct"
:model="model"
:manufacturers="manufacturers"
:isEditing="true"
></product-form>
</div>
</template>
<script>
import ProductForm from '@/components/products/ProductForm.vue';
export default {
data: {
model() {
const product = this.$store.getters.productById(this.$route.params['id']);
// Return the copy of product, so that after modifying the copy of product, the product property of the local Vuex stire is not modified before saving
return { ...product, manufacturer: { ...product.manufacturer } };
}
},
created() {
const { name } = this.model;
if(! name) {this.$store.dispatch('productById', {
productId: this.$route.params['id']}); }if (this.manufacturers.length === 0) {
this.$store.dispatch('allManufacturers'); }},computed: {
manufacturers() {
return this.$store.getters.allManufacturers; }},methods: {
updateProduct(product) {
this.$store.dispatch('updateProduct', {
product,
})
}
},
components: {
'product-form': ProductForm
}
}
</script>
Copy the code
Here we change the calculated property model to data, because we found that if the product object model is passed to the child ProductForm as a calculated property, the form cannot be edited. You can run it and try to see if it can be edited. Our initial guess was that the form data object Model in the el-Form component could not be derived from computed properties, otherwise it could not be edited. Therefore, we first tried to put the computed property Model in the component into the data property.
Enter the ProductForm component again for refactoring. Here we use the El-Form component provided by the Element-UI component library to replace the normal form to display the product information.
<template>
<div class="productInfo">
<el-form class="form" ref="form" :model="model" label-width="180px">
<el-form-item label="Name">
<el-input v-model="model.name"></el-input>
</el-form-item>
<el-form-item label="Price">
<el-input v-model="model.price"></el-input>
</el-form-item>
<el-form-item label="Manufacturer ">
<el-select v-model="model.manufacturer.name" clearable placeholder="Please select manufacturer">
<el-option
v-for="manufacturer in manufacturers"
:key="manufacturer._id"
:label="manufacturer.name"
:value="manufacturer.name">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="Image ">
<el-input v-model="model.image"></el-input>
</el-form-item>
<el-form-item label="Description ">
<el-input type="textarea" v-model="model.description"></el-input>
</el-form-item>
<el-form-item>
<el-button v-if="isEditing" type="primary" @click="onSubmit">Update Product</el-button>
<el-button v-else @click="onSubmit">Add Product</el-button>
</el-form-item>
</el-form>
</div>
// ...
</template>
<script>
export default {
props: ['model'.'manufacturers'.'isEditing'],
created() {
console.log(this.model)
},
methods: {
onSubmit() {
this.$emit('save-product'.this.model)
}
}
}
</script>
<style>
.productInfo {
padding-top: 10px;
}
.form {
margin: 0 auto;
width: 500px;
}
.el-input__inner {
height: 60px;
}
</style>
Copy the code
We believe that you can also easily understand the use of the EL-Form component, where the model property represents the form data object, we can use v-Model to bidirectionally bind the information in the form data object to the corresponding form components. A special reminder that the manufacturer in the product object Model is a manufacturer object that contains the manufacturer ID and name attributes.
Now let’s go to the New component for refactoring. Once we found problems in the Edit component, we also tried to define the calculated property Model in that component into the data property.
<template>
<product-form
@save-product="addProduct"
:model="model"
:manufacturers="manufacturers"
>
</product-form>
</template>
<script>
import ProductForm from '@/components/products/ProductForm.vue';
export default {
data() {
return {
model: {manufacturer: {name: ' '}}
}
},
created() {
if (this.manufacturers.length === 0) {
this.$store.dispatch('allManufacturers'); }},computed: {
manufacturers() {
return this.$store.getters.allManufacturers; }},methods: {
addProduct(model) {
this.$store.dispatch('addProduct', {
product: model,
})
},
},
components: {
'product-form': ProductForm
}
}
</script>
Copy the code
Because this component is a newly created goods component, we define an empty object model, but we need to give it a default initial form model: {manufacturer: {name: “}} to prevent errors when the name property is not accessible in the child component form.
The form for adding or modifying product information now looks like this:
Refactoring the manufacturer information function
The manufacturer information function includes manufacturer information display, manufacturer information addition and manufacturer information modification. Like the commodity information reconstruction function, we also use the components provided by the Element-UI component library for reconstruction, so as to improve the user’s interactive experience when operating manufacturer information.
First we went into the Manufacturers component for refactoring. Similar to the Products component, we used the El-Table component provided by the Element-UI component library to replace the normal table showing the manufacturer information list.
<template>
<div class="manufacturers">
<el-table
class="table"
:data="manufacturers">
<el-table-column
prop="name"
label="Manufacturer"
width="180">
</el-table-column>
<el-table-column
label="Operation"
width="200">
<template slot-scope="scope">
<el-button class="modify" type="text" size="small"><router-link :to="'/admin/manufacturers/edit/' + scope.row._id">Modify the</router-link></el-button>
<el-button class="remove" @click="removeManufacturer(scope.row._id), deleteRow(scope.$index, products)" type="text" size="small">delete</el-button>
</template>
</el-table-column>
</el-table>
// ...
</div>
</template>
// ...
<script>
export default {
created() {
this.$store.dispatch('allManufacturers');
},
computed: {
manufacturers() {
return this.$store.getters.allManufacturers
}
},
methods: {
removeManufacturer(manufacturerId) {
// Use the confirm method of the JavaScript BOM to ask the user whether to delete the manufacturer
const res = confirm('Do you want to delete this manufacturer? ');
// If the user agrees, delete the manufacturer
if (res) {
this.$store.dispatch('removeManufacturer', {
manufacturerId,
})
}
}
}
}
</script>
Copy the code
Here is the list of manufacturers we reconstructed:
Enter the NewManufacturers component again for refactoring, again putting the defined calculation property Model into the data property.
<template>
<manufacturer-form
@save-manufacturer="addManufacturer"
:model="model"
>
</manufacturer-form>
</template>
<script>
import ManufacturerForm from '@/components/ManufacturerForm.vue';
export default {
data() {
return {
model: {}}}.methods: {
addManufacturer(model) {
this.$store.dispatch('addManufacturer', {
manufacturer: model,
})
},
},
components: {
'manufacturer-form': ManufacturerForm
}
}
</script>
Copy the code
The ManufacturerForm subcomponent will then be refactored into the ManufacturerForm subcomponent. Similar to the ProductForm component, the el-Form component provided by the Elder-UI component library will replace the generic form to display manufacturer information.
<template>
<div class="manufacturerInfo">
<el-form class="form" ref="form" :model="model" label-width="180px">
<el-form-item label="Name">
<el-input v-model="model.name"></el-input>
</el-form-item>
<el-form-item>
<el-button v-if="isEditing" type="primary" @click="onSubmit">Update Manufacturer</el-button>
<el-button v-else @click="onSubmit">Add Manufacturer</el-button>
</el-form-item>
</el-form>
</div>
// ...
</template>
<script>
export default {
props: ['model'.'isEditing'].methods: {
onSubmit() {
this.$emit('save-manufacturer'.this.model)
}
}
}
</script>
// ...
Copy the code
This is the form interface when we reconstructed the user to add or modify the manufacturer information:
Finally, we go into the Cart component for refactoring, and we see that this component is very similar to the ProductList component because both reuse the ProductItem child, which is used to display a list of Cart items.
<template>
<div>
<div class="title">
<h1>{{msg}}</h1>
</div>
<product-item :products="cart"></product-item>
</div>
</template>
<script>
import ProductItem from '@/components/products/ProductItem.vue';
export default {
name: 'home',
data () {
return {
msg: 'Welcome to the Cart Page'}},computed: {
cart() {
return this.$store.state.cart; }},components: {
'product-item': ProductItem
}
}
</script>
Copy the code
Here is the shopping cart interface after refactoring:
summary
In this section, we mainly use the Element-UI component library to reconstruct the project code and upgrade the navigation bar of the home page, commodity information function, manufacturer information function and shopping cart page to improve the user’s interactive experience. However, this also leads to partial paralysis of functional logic, which we will take you to solve in the next section.
Fix element- UI form bidirectional binding
In the previous section, we used the Element-UI component library to complete the project code refactoring, but when we ran the project, we found that the form information was still uneditable, indicating that our previous attempt failed. Undeterred, we continue to try, and in this section we try a new way to fix the element-UI form bidirectional binding problem.
The question you should have is something like this:
Refactoring the Edit component
We first go to the Edit component for the fix, where we mainly restore the original data definition.
<template>
<div>
<div class="title">
<h1>This is Admin/Edit</h1>
</div>
<product-form
@save-product="updateProduct"
:model="model"
:manufacturers="manufacturers"
:isEditing="true"
></product-form>
</div>
</template>
<script>
import ProductForm from "@/components/products/ProductForm.vue";
export default {
created() {
const { name = "" } = this.modelData || {};
if(! name) {this.$store.dispatch("productById", {
productId: this.$route.params["id"]}); }if (this.manufacturers.length === 0) {
this.$store.dispatch("allManufacturers"); }},computed: {
manufacturers() {
return this.$store.getters.allManufacturers;
},
model() {
const product = this.$store.getters.productById(this.$route.params["id"]);
constres = { ... product,manufacturer: { ...product.manufacturer } };
returnres; }},methods: {
updateProduct(product) {
this.$store.dispatch("updateProduct", { product }); }},components: {
"product-form": ProductForm
}
};
</script>
Copy the code
The model object replaced in the data attribute is restored to the calculation attribute, which is used to cache the model object information and improve performance. We are going to fix the form uneditable issue in the ProductForm component below.
Refactor the ProductForm component
Once again in the ProductForm component, let’s try another approach to fix the form’s uneditable problem.
<template>
<div class="productInfo">
<el-form class="form" ref="form" label-width="180px">
<el-form-item label="Name">
<el-input v-model="model.name"></el-input>
</el-form-item>
<el-form-item label="Price">
<el-input v-model="model.price"></el-input>
</el-form-item>
<el-form-item label="Manufacturer ">
<el-select
v-model="modelData.manufacturer.name"
clearable
placeholder="Please select manufacturer"
>
<el-option
v-for="manufacturer in manufacturers"
:key="manufacturer._id"
:label="manufacturer.name"
:value="manufacturer.name"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="Image ">
<el-input v-model="model.image"></el-input>
</el-form-item>
<el-form-item label="Description ">
<el-input type="textarea" v-model="model.description"></el-input>
</el-form-item>
<el-form-item>
<el-button v-if="isEditing" type="primary" @click="onSubmit"
>Update Product</el-button
>
<el-button v-else @click="onSubmit">Add Product</el-button>
</el-form-item>
</el-form>
</div>
// ...
</template>
// ...
<script>
export default {
data() {
return {
modelData: { manufacturer: { name: ""}}}; },props: ["model"."manufacturers"."isEditing"],
created() {
const product = this.model;
this.modelData = { ... product,manufacturer: { ...product.manufacturer } };
},
watch: {
model(val, oldVal) {
this.modelData = val; }},methods: {
onSubmit() {
this.$emit("save-product".this.modelData); }}};</script>
// ...
Copy the code
Instead of using the Model object fetched from the parent component directly as the form data object, we’ll customize a modelData object in that component and use the default initial form. Then, when the component is first created, the Model object obtained from the parent component is assigned to a temporary variable called Product, which is then shallowly copied into the modelData object, thus avoiding the use of computed properties for the form data object. But it’s just finished half of the work, because we need to implement two-way binding effect, so we need to monitor the change of the form components, by using the method of watch monitoring user input, and then store the new data to modelData object, thus successfully implement the two-way binding, and the form can be edited freely.
But we are only trying to use the modelData object in the drop-down menu here, so we will use this object for the entire in-form component later.
summary
In this section, we mainly repaired the problem of element-UI form bidirectional binding, realized the bidirectional binding of form data through the custom modelData object and the watch method to monitor the change of form data, and solved the problem that the form could not be edited. But just try it in the drop-down menu, and later we’ll refactor the entire product information form component.
Improve form bidirectional binding
Refactor the ProductForm component
Entering the ProductForm component again, we need to perfect the problem left in the previous section, that is, we only tried the drop-down menu in the commodity information form, and the attempt was successful. Therefore, in this section, we need to import modelData objects into all components in the form to solve the problem that components in other forms cannot be edited.
<template>
<div class="productInfo">
<el-form class="form" ref="form" label-width="180px">
<el-form-item label="Name">
<el-input v-model="modelData.name"></el-input>
</el-form-item>
<el-form-item label="Price">
<el-input v-model="modelData.price"></el-input>
</el-form-item>
<el-form-item label="Manufacturer ">
<el-select
v-model="modelData.manufacturer.name"
clearable
placeholder="Please select manufacturer"
>
<el-option
v-for="manufacturer in manufacturers"
:key="manufacturer._id"
:label="manufacturer.name"
:value="manufacturer.name"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="Image ">
<el-input v-model="modelData.image"></el-input>
</el-form-item>
<el-form-item label="Description ">
<el-input type="textarea" v-model="modelData.description"></el-input>
</el-form-item>
<el-form-item>
<el-button v-if="isEditing" type="primary" @click="onSubmit"
>Update Product</el-button
>
<el-button v-else @click="onSubmit">Add Product</el-button>
</el-form-item>
</el-form>
</div>
// ...
</template>
// ...
<script>
export default {
data() {
return {
modelData: { manufacturer: { name: ""}}}; },props: ["model"."manufacturers"."isEditing"],
created() {
const product = this.model;
// ...
this.modelData = { ... product,manufacturer: { ...product.manufacturer } };
},
watch: {
model(val, oldVal) {
this.modelData = val; }},methods: {
onSubmit() {
this.$emit("save-product".this.modelData); }}};</script>
// ...
Copy the code
summary
In this section, we take you through the problem left over from the previous section, that is, copying the attempts from the drop-down menu to other form components to ensure that the entire form component can be edited smoothly.
Solve the error of commodity information form operation
Refactor the ProductForm component
I believe that when you add or modify the commodity information form, the console will appear an error that the ID attribute is not defined. We should first enter the error component for debugging, you should see the error message appears in the ProductForm component. So we need to go into the ProductForm component to debug.
<template>
<div class="productInfo">
<el-form class="form" ref="form" label-width="180px">
<el-form-item label="Name">
<el-input v-model="modelData.name"></el-input>
</el-form-item>
<el-form-item label="Price">
<el-input v-model="modelData.price"></el-input>
</el-form-item>
<el-form-item label="Manufacturer ">
<el-select
v-model="modelData.manufacturer.name"
clearable
placeholder="Please select manufacturer"
>
<el-option
v-for="manufacturer in manufacturers"
:key="manufacturer._id"
:label="manufacturer.name"
:value="manufacturer.name"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="Image ">
<el-input v-model="modelData.image"></el-input>
</el-form-item>
<el-form-item label="Description ">
<el-input type="textarea" v-model="modelData.description"></el-input>
</el-form-item>
<el-form-item>
<el-button v-if="isEditing" type="primary" @click="onSubmit"
>Update Product</el-button
>
<el-button v-else @click="onSubmit">Add Product</el-button>
</el-form-item>
</el-form>
</div>
// ...
</template>
// ...
<script>
export default {
data() {
return {
modelData: { manufacturer: { name: ""}}}; },props: ["model"."manufacturers"."isEditing"],
created() {
const product = this.model;
// ...
this.modelData = { ... product,manufacturer: { ...product.manufacturer } };
},
watch: {
model(val, oldVal) {
this.modelData = val; }},methods: {
onSubmit() {
const manufacturer = this.manufacturers.find(item= > item.name === this.modelData.manufacturer.name);
this.modelData.manufacturer = manufacturer;
this.$emit("save-product".this.modelData); }}};</script>
// ...
Copy the code
First of all, you should know that the goods object also contains the corresponding manufacturer object, and the manufacturer object contains the ID attribute and the name attribute. However, we should find that the drop-down menu in the commodity information form is bidirectionally bound to the name attribute of the manufacturer object in the commodity object, so the manufacturer object stored in the modelData object in the watch method also has only the name attribute. But the backend database requires that the manufacturer object must also have an ID attribute, which is why we get an error when we click update item information.
Here we use the find method of the local manufacturer array to retrieve the manufacturer object with the corresponding name and overwrite the manufacturer object in the modelData object, so that the manufacturer object in our modelData object is an object that meets the requirements of the back-end database.
summary
In this section, we analyze and try to solve the problem that the id attribute is not defined when operating the commodity information form.
Add dynamic effects and message prompt
We noticed when a user to add or modify products or manufacturers information, hard to avoid can update delay problems, this time if there is no feedback on a page will look a little embarrassed, so we think that as long as the user to add or modify the operation, the back-end data synchronization is complete before we for the page to add a dynamic load effect, A feedback to the user indicates that the data is being processed, please wait patiently; In addition, after the back-end synchronization is completed, a message prompt is added to the page to give users a feedback indicating the success of data processing, thus avoiding embarrassing scenes and improving the user’s interactive experience.
Loading dynamic loading effect
Access the ManufactureForm component again to enable dynamic loading when users add or modify manufacturer information and before data synchronization is complete on the back-end.
<template>
<div class="manufacturerInfo">
<el-form
class="form"
ref="form"
label-width="180px"
v-loading="loading"
element-loading-text="Loading like hell."
element-loading-spinner="el-icon-loading"
element-loading-background="Rgba (0, 0, 0, 0.8)">
<el-form-item label="Name">
<el-input v-model="manufacturerData.name"></el-input>
</el-form-item>
<el-form-item>
<el-button v-if="isEditing" type="primary" native-type="submit" @click="onSubmit">Update Manufacturer</el-button>
<el-button v-else @click="onSubmit">Add Manufacturer</el-button>
</el-form-item>
</el-form>
</div>
// ...
</template>
// ...
<script>
export default {
props: ['model'.'isEditing'],
data() {
return {
manufacturerData: {name: ' '}
}
},
created() {
this.manufacturerData = this.model
},
watch: {
model(val, oldVal) {
this.manufacturerData = val; }},computed: {
loading() {
return this.$store.state.showLoader
}
},
methods: {
onSubmit() {
this.$emit('save-manufacturer'.this.manufacturerData); }}}</script>
// ...
Copy the code
First of all, we use the custom instruction v-loading provided by the Element-UI component library in this component, and determine whether to achieve dynamic loading effect by checking whether loading is true or false. In this case, the showLoader property in the local state is obtained as the loading property value, because when the user just adds or modifates, the data request is initiated from the back end, and the value of the showLoader property in the local state is true. When the data response is successfully obtained, The value of the showLoader property is false, which enables the dynamic loading effect to be displayed at the specified time. In addition, we also modified the data handling of the ProductForm component to address the editing issues of the manufacturer’s form component.
Similarly, enter ProductForm component for modification, so that when users add or modify product information, and before the back-end data synchronization is completed, the page can be loaded dynamically.
<template>
<div class="productInfo">
<el-form
class="form"
ref="form"
label-width="180px"
v-loading="loading"
element-loading-text="Loading like hell."
element-loading-spinner="el-icon-loading"
element-loading-background="Rgba (0, 0, 0, 0.8)">
<el-form-item label="Name">
<el-input v-model="modelData.name"></el-input>
</el-form-item>
<el-form-item label="Price">
<el-input v-model="modelData.price"></el-input>
</el-form-item>
<el-form-item label="Manufacturer ">
<el-select
v-model="modelData.manufacturer.name"
clearable
placeholder="Please select manufacturer"
>
<el-option
v-for="manufacturer in manufacturers"
:key="manufacturer._id"
:label="manufacturer.name"
:value="manufacturer.name"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="Image ">
<el-input v-model="modelData.image"></el-input>
</el-form-item>
<el-form-item label="Description ">
<el-input type="textarea" v-model="modelData.description"></el-input>
</el-form-item>
<el-form-item>
<el-button v-if="isEditing" type="primary" native-type="submit" @click="onSubmit"
>Update Product</el-button
>
<el-button v-else @click="onSubmit">Add Product</el-button>
</el-form-item>
</el-form>
</div>
// ...
</template>
// ...
<script>
export default {
data() {
return {
modelData: { manufacturer: { name: ""}}}; },props: ["model"."manufacturers"."isEditing"],
created() {
const product = this.model;
this.modelData = { ... product,manufacturer: { ...product.manufacturer } };
},
watch: {
model(val, oldVal) {
this.modelData = val; }},computed: {
loading() {
return this.$store.state.showLoader
}
},
methods: {
onSubmit() {
/ / since the form only bind the modelData. Manufacturer. The name,
Manufacturer._id is missing, but the manufacturer object is needed at the back end.
// Find and overwrite the manufacturer of manufacturers into modelData
const manufacturer = this.manufacturers.find(item= > item.name === this.modelData.manufacturer.name);
this.modelData.manufacturer = manufacturer;
this.$emit("save-product".this.modelData); }}};</script>
// ...
Copy the code
Realize message prompt function
Start by going to the actions.js file, which is where the sending of network request data is performed, so we can add message prompt functionality.
import axios from 'axios';
import {
// ...
} from './mutation-types';
import { Message } from 'element-ui';
// ...
const API_BASE = 'http://localhost:3000/api/v1';
// ...
export const productActions = {
// ...
removeProduct({ commit }, payload) {
commit(REMOVE_PRODUCT);
const { productId } = payload;
axios.delete(`${API_BASE}/products/${productId}`)
.then((a)= > {
// return productId, used to delete the local corresponding item
commit(REMOVE_PRODUCT_SUCCESS, {
productId,
});
Message({
message: 'Congratulations, the product deleted successfully! '.type: 'success'
})
})
.catch((a)= > {
Message.error('Sorry, failed to delete goods! ');
})
},
updateProduct({ commit }, payload) {
commit(UPDATE_PRODUCT);
const { product } = payload;
axios.put(`${API_BASE}/products/${product._id}`, product)
.then(response= > {
commit(UPDATE_PRODUCT_SUCCESS, {
product: response.data,
});
Message({
message: 'Congratulations, product update success! '.type: 'success'
})
})
.catch((a)= > {
Message.error('Sorry, product update failed! ');
})
},
addProduct({ commit }, payload) {
commit(ADD_PRODUCT);
const { product } = payload;
axios.post(`${API_BASE}/products`, product)
.then(response= > {
commit(ADD_PRODUCT_SUCCESS, {
product: response.data,
})
Message({
message: 'Congratulations, product added successfully! '.type: 'success'
})
})
.catch((a)= > {
Message.error('Sorry, failed to add goods! '); })}};// ...
export const manufacturerActions = {
// ...
removeManufacturer({ commit }, payload) {
commit(REMOVE_MANUFACTURER);
const { manufacturerId } = payload;
axios.delete(`${API_BASE}/manufacturers/${manufacturerId}`)
.then((a)= > {
// Return the manufacturerId to delete the local manufacturer
commit(REMOVE_MANUFACTURER_SUCCESS, {
manufacturerId,
});
Message({
message: 'Congratulations, manufacturer deleted successfully! '.type: 'success'
})
})
.catch((a)= > {
Message.error('Sorry, manufacturer delete failed! ');
})
},
updateManufacturer({ commit }, payload) {
commit(UPDATE_MANUFACTURER);
const { manufacturer } = payload;
axios.put(`${API_BASE}/manufacturers/${manufacturer._id}`, manufacturer)
.then(response= > {
commit(UPDATE_MANUFACTURER_SUCCESS, {
manufacturer: response.data,
});
Message({
message: 'Congratulations, manufacturer update successful! '.type: 'success'
})
})
.catch((a)= > {
Message.error('Sorry, manufacturer update failed! ');
})
},
addManufacturer({ commit }, payload) {
commit(ADD_MANUFACTURER);
const { manufacturer } = payload;
axios.post(`${API_BASE}/manufacturers`, manufacturer)
.then(response= > {
commit(ADD_MANUFACTURER_SUCCESS, {
manufacturer: response.data,
});
Message({
message: 'Congratulations, manufacturer added successfully! '.type: 'success'
})
})
.catch((a)= > {
Message.error('Sorry, manufacturer add failed! '); }}})Copy the code
Here we first import the Message notification component provided by the Element-UI component library and add a success Message notification when the network request is successful and a failure Message notification when the request is failed.
Then go to the retros.js file to make the changes, which add a message prompt for local shopping cart data processing.
import {
// ...
} from './mutation-types';
import { Message } from 'element-ui';
// ...
export const cartMutations = {
[ADD_TO_CART](state, payload) {
const { product } = payload;
state.cart.push(product);
Message({
message: 'Congratulations, added to shopping cart! '.type: 'success'
})
},
[REMOVE_FROM_CART](state, payload) {
const { productId } = payload
state.cart = state.cart.filter(product= >product._id ! == productId) Message({message: 'Congratulations, you successfully removed the shopping cart! '.type: 'success'})}};// ...
Copy the code
Similarly, we first need to import the Message notification component provided by the Element-UI component library. When the user adds or removes the shopping cart, the Message notification will be executed.
We get feedback when we add, remove, modify, and add or remove shopping carts:
summary
In this section, we mainly do the following work:
- Add for the form component
element-ui
The component library providesv-loading
Instruction to achieve dynamic loading effect; - added
element-ui
The component library providesMessage
Message prompt component, realize the feedback message prompt after the user operates the form information.
The updated form information cannot be displayed after modification
Reconstruction to here I believe that some friends have been unable to wait to run the project, but always backfire, but we do not have the slightest side, as long as you follow us step by step down-to-earth to analyze the problem, then what problems will be solved. The problem is that when a user modifies information about a product or manufacturer, the old information is displayed after the user clicks the update button.
Here’s what you might encounter:
When the user updates the form information, it should first initiate a network request to the back end, and then synchronize the latest data to the local state for display. Therefore, we come to the actions.js file for debugging.
Submit the latest data
Once again entering the actions.js file for debugging, we can make a bold guess that the data object submitted to the retro.js file after the successful network request is not the latest data modified by the user.
import axios from 'axios';
// ...
import {
// ...
} from './mutation-types';
import { Message } from 'element-ui';
// ...
const API_BASE = 'http://localhost:3000/api/v1';
// ...
export const productActions = {
// ...
updateProduct({ commit }, payload) {
commit(UPDATE_PRODUCT);
// ...
const { product } = payload;
axios.put(`${API_BASE}/products/${product._id}`, product)
.then(response= > {
commit(UPDATE_PRODUCT_SUCCESS, {
product: product,
});
Message({
message: 'Congratulations, product update success! '.type: 'success'
})
})
.catch((a)= > {
Message.error('Sorry, product update failed! '); })},// ...
};
// ...
export const manufacturerActions = {
// ...
updateManufacturer({ commit }, payload) {
commit(UPDATE_MANUFACTURER);
// ...
const { manufacturer } = payload;
axios.put(`${API_BASE}/manufacturers/${manufacturer._id}`, manufacturer)
.then(response= > {
commit(UPDATE_MANUFACTURER_SUCCESS, {
manufacturer: manufacturer,
});
Message({
message: 'Congratulations, manufacturer update successful! '.type: 'success'
})
})
.catch((a)= > {
Message.error('Sorry, manufacturer update failed! '); })},// ...
}
Copy the code
Here, we changed the payload submitted when the network request was successful to the latest data object, and then submitted it to the corresponding type of mutation for local data update.
Synchronize the latest data to the local computer
Next, we need to access the retro.js file to synchronize the latest data it gets to the local state.
import {
// ...
} from './mutation-types';
import { Message } from 'element-ui';
// ...
export const productMutations = {
// ...
[UPDATE_PRODUCT_SUCCESS](state, payload) {
state.showLoader = false;
const { product: newProduct } = payload;
// ...
state.products = state.products.map( product= > {
if (product._id === newProduct._id) {
return newProduct;
}
return product;
});
state.product = newProduct;
},
// ...
[UPDATE_MANUFACTURER_SUCCESS](state, payload) {
state.showLoader = false;
// ...
const { manufacturer: newManufacturer } = payload;
state.manufacturers = state.manufacturers.map(manufacturer= > {
if (manufacturer._id === newManufacturer._id) {
return newManufacturer;
}
return manufacturer;
});
state.manufacturer = newManufacturer;
},
// ...
}
Copy the code
summary
In this section, we mainly analyze and try to solve the problem that the latest information cannot be displayed after the form information is modified.
This tutorial shows you some of the problems encountered in refactoring e-commerce application front-end code using the Element-UI component library, and we walk you through the analysis and try to solve the problems step by step. Hopefully, this tutorial has given you an overview of some of the issues you need to be aware of when using the Element-UI component library, which is the ability to analyze and try to solve them. Well, at this point, our project can basically run happily, and the user’s interactive experience has been significantly improved.
If you encounter other problems during the project operation, please don’t be stingy with your questions and communicate with us as much as possible!
Want to learn more exciting practical skills tutorial? Come and visit the Tooquine community.
The source code for this article is available on Github. If you think it is well written, please give ❤️ a thumbs up +Github repository plus a star ❤️