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:
JavaScript
x
49
49
1
<template>
2
<div class="dropdown">
3
<button
4
@click="toggleShow(); $emit('sortPrice')"
5
class="dropbtn"
6
>
7
{{ title }}
8
<span class="material-icons-outlined"> {{ icon }} </span>
9
</button>
10
11
<div v-if="showMenu" class="menu">
12
<div class="menu-item" v-for="(item, index) in this.items" :key="index">
13
{{ item }}
14
</div>
15
</div>
16
</div>
17
</template>
18
19
<script>
20
export default {
21
name: "Dropdown-menu",
22
23
props: {
24
title: String,
25
icon: String,
26
items: {
27
type: Object,
28
required: true,
29
},
30
},
31
32
data() {
33
return {
34
showMenu: false
35
};
36
},
37
38
methods: {
39
toggleShow: function () {
40
this.showMenu = !this.showMenu;
41
},
42
43
sortPrice: function () {
44
this.$emit("sort", this.sortPrice);
45
},
46
},
47
};
48
</script>
49
This Parent Component:
JavaScript
1
41
41
1
<template>
2
<dropdown
3
:title="sortedBy"
4
:items="arrangements"
5
:icon="material_icons"
6
@sort="sortByPrice"
7
></dropdown>
8
</template>
9
10
<script>
11
import Dropdown from "@/components/Dropdown.vue";
12
13
export default {
14
components: {
15
Dropdown,
16
},
17
18
data() {
19
return {
20
sortedBy: "Featured",
21
arrangements: ["Featured", "Lowest", "Highest"],
22
material_icons: "expand_more",
23
productData: require("@/data/store-data.json"),
24
};
25
},
26
methods: {
27
sortByPrice: function () {
28
let realProducts = this.productData.products;
29
let sortedProducts = realProducts.sort((a, b) => {
30
if (this.sortedBy === "Highest") {
31
return b.price - a.price;
32
} else if (this.sortedBy === "Lowest") {
33
return a.price - b.price;
34
}
35
});
36
return sortedProducts;
37
},
38
},
39
};
40
</script>
41
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:
JavaScript
1
62
62
1
<template>
2
<h2>Main App</h2>
3
<dropdown
4
:title="sortedBy"
5
:items="arrangements"
6
@sort="sortByPrice"
7
></dropdown>
8
<div>
9
<h3>Products</h3>
10
<ul>
11
<li v-for="product in sortedProducts" :key="product.index">
12
{{ product.name }} ${{ product.price }}
13
</li>
14
</ul>
15
</div>
16
</template>
17
18
<script>
19
import Dropdown from "@/components/Dropdown.vue";
20
21
export default {
22
components: {
23
Dropdown,
24
},
25
26
data() {
27
return {
28
sortedBy: "Featured",
29
arrangements: ["Featured", "Lowest", "Highest"],
30
productData: {
31
// dummy data for demo purposes
32
products: [
33
{ index: 1, name: "product A", price: 1, featured: true },
34
{ index: 2, name: "product B", price: 2, featured: false },
35
{ index: 3, name: "product C", price: 6, featured: true },
36
{ index: 4, name: "product G", price: 4, featured: false },
37
{ index: 5, name: "product V", price: 0, featured: true },
38
],
39
},
40
};
41
},
42
methods: {
43
sortByPrice: function (item) {
44
this.sortedBy = item;
45
},
46
},
47
computed: {
48
sortedProducts: function () {
49
if (this.sortedBy === "Featured") {
50
return this.productData.products.filter((prod) => prod.featured);
51
} else if (this.sortedBy === "Highest") {
52
return this.productData.products.sort((a, b) => b.price - a.price);
53
} else if (this.sortedBy === "Lowest") {
54
return this.productData.products.sort((a, b) => a.price - b.price);
55
}
56
// the "just-in-case" default return
57
return this.productData.products;
58
},
59
},
60
};
61
</script>
62
and the child Dropdown.vue component:
JavaScript
1
50
50
1
<template>
2
<div class="dropdown">
3
<button @click="toggleShow()" class="dropbtn">
4
{{ title }}
5
</button>
6
7
<div v-if="showMenu" class="menu">
8
<div
9
class="menu-item"
10
v-for="(item, index) in this.items"
11
:key="index"
12
@click="sortPrice(item)"
13
>
14
{{ item }}
15
</div>
16
</div>
17
</div>
18
</template>
19
20
<script>
21
export default {
22
name: "Dropdown-menu",
23
24
props: {
25
title: String,
26
items: {
27
type: Object,
28
required: true,
29
},
30
},
31
32
data() {
33
return {
34
showMenu: false,
35
};
36
},
37
38
methods: {
39
toggleShow: function () {
40
this.showMenu = !this.showMenu;
41
},
42
43
sortPrice: function (item) {
44
this.$emit("sort", item);
45
this.toggleShow();
46
},
47
},
48
};
49
</script>
50