This is the first project for me in VueJS. I have a product list and want to sort it by price. I built two components and tried to pass a sort method to the parent from the child component (dropdown button) by emitting an event. but after a lot of attempts, I can't find the wrong with my code, any help!
This Child Component:
<template>
<div >
<button
@click="toggleShow(); $emit('sortPrice')"
>
{{ title }}
<span > {{ icon }} </span>
</button>
<div v-if="showMenu" >
<div v-for="(item, index) in this.items" :key="index">
{{ item }}
</div>
</div>
</div>
</template>
<script>
export default {
name: "Dropdown-menu",
props: {
title: String,
icon: String,
items: {
type: Object,
required: true,
},
},
data() {
return {
showMenu: false
};
},
methods: {
toggleShow: function () {
this.showMenu = !this.showMenu;
},
sortPrice: function () {
this.$emit("sort", this.sortPrice);
},
},
};
</script>
This Parent Component:
<template>
<dropdown
:title="sortedBy"
:items="arrangements"
:icon="material_icons"
@sort="sortByPrice"
></dropdown>
</template>
<script>
import Dropdown from "@/components/Dropdown.vue";
export default {
components: {
Dropdown,
},
data() {
return {
sortedBy: "Featured",
arrangements: ["Featured", "Lowest", "Highest"],
material_icons: "expand_more",
productData: require("@/data/store-data.json"),
};
},
methods: {
sortByPrice: function () {
let realProducts = this.productData.products;
let sortedProducts = realProducts.sort((a, b) => {
if (this.sortedBy === "Highest") {
return b.price - a.price;
} else if (this.sortedBy === "Lowest") {
return a.price - b.price;
}
});
return sortedProducts;
},
},
};
</script>
CodePudding user response:
Suggestions:
- emit when an individual item is clicked, not when the button is clicked. You want to emit when the user makes a selection
- So this means calling the sortPrice function from the menu-item div via
@click="sortPrice(item)"
- Then in the sortPrice function, pass in the item paramter,
function (item) {
and pass it as a second parameter to your emit call:this.$emit("sort", item);
. The parent must know what was selected - In the parent component, sortByPrice function, accept the item parameter,
sortByPrice: function (item) {
and use it to set the sortedBy property:this.sortedBy = item;
- Do the sorting in a computed property that is then displayed, here in my example called
sortedProducts
.
For example, the parent:
<template>
<h2>Main App</h2>
<dropdown
:title="sortedBy"
:items="arrangements"
@sort="sortByPrice"
></dropdown>
<div>
<h3>Products</h3>
<ul>
<li v-for="product in sortedProducts" :key="product.index">
{{ product.name }} ${{ product.price }}
</li>
</ul>
</div>
</template>
<script>
import Dropdown from "@/components/Dropdown.vue";
export default {
components: {
Dropdown,
},
data() {
return {
sortedBy: "Featured",
arrangements: ["Featured", "Lowest", "Highest"],
productData: {
// dummy data for demo purposes
products: [
{ index: 1, name: "product A", price: 1, featured: true },
{ index: 2, name: "product B", price: 2, featured: false },
{ index: 3, name: "product C", price: 6, featured: true },
{ index: 4, name: "product G", price: 4, featured: false },
{ index: 5, name: "product V", price: 0, featured: true },
],
},
};
},
methods: {
sortByPrice: function (item) {
this.sortedBy = item;
},
},
computed: {
sortedProducts: function () {
if (this.sortedBy === "Featured") {
return this.productData.products.filter((prod) => prod.featured);
} else if (this.sortedBy === "Highest") {
return this.productData.products.sort((a, b) => b.price - a.price);
} else if (this.sortedBy === "Lowest") {
return this.productData.products.sort((a, b) => a.price - b.price);
}
// the "just-in-case" default return
return this.productData.products;
},
},
};
</script>
and the child Dropdown.vue component:
<template>
<div >
<button @click="toggleShow()" >
{{ title }}
</button>
<div v-if="showMenu" >
<div
v-for="(item, index) in this.items"
:key="index"
@click="sortPrice(item)"
>
{{ item }}
</div>
</div>
</div>
</template>
<script>
export default {
name: "Dropdown-menu",
props: {
title: String,
items: {
type: Object,
required: true,
},
},
data() {
return {
showMenu: false,
};
},
methods: {
toggleShow: function () {
this.showMenu = !this.showMenu;
},
sortPrice: function (item) {
this.$emit("sort", item);
this.toggleShow();
},
},
};
</script>