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 class="dropdown"> <button @click="toggleShow(); $emit('sortPrice')" class="dropbtn" > {{ title }} <span class="material-icons-outlined"> {{ icon }} </span> </button> <div v-if="showMenu" class="menu"> <div class="menu-item" 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>
Advertisement
Answer
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 class="dropdown"> <button @click="toggleShow()" class="dropbtn"> {{ title }} </button> <div v-if="showMenu" class="menu"> <div class="menu-item" 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>