I have a Vue.js application which loads a list of items, and each item is passed as a prop
to a Vue component.
I figured out that by using mixins
I can share common component properties, like computed
,created
, etc.
Now, I'm trying to sort the list of items and can't figure out how I would access each component's computed properties to apply sorting/filtering. How can I accomplish this?
Items
[{
price: 10,
qty: 2
}, {
price: 8,
qty: 3
}]
Mixin - ./Cost.js
export default {
computed: {
cost () {
return this.price * this.qty;
}
}
}
Component (which works as expected) - ./Product.vue
import Cost from './Cost.js'
export default {
name: 'product-item',
props: ['product'],
mixins: [Cost]
}
How would you access the computed properties, or restructure this setup?
List component
<template>
<div id="list">
<div v-for="product in sorted" :product="product">Cost: {{ cost }} </div>
</div>
</template>
<script>
import ProductItem from './Product.vue'
export default {
components: { ProductItem },
created: () {
this.items = [...] // as noted above
},
computed: {
sorted () {
return this.items.sort( (a,b) => b.cost - a.cost); // cost is not accessible!
}
}
}
</script>
Use vuex
. Your vuex store
will provide a getters
object that can be wrapped into multiple components’ native computed
objects, or accessed directly. Your code will be DRY, reactive, cached, and maintainable.
From my experience, once you need to go beyond child-parent
data relationships, vuex
, store
, and shared state
are the way to go. Once you get the hang of it, it is downright magical how your app evolves.
It is beyond scope of the question to show how to install vuex
. Visit https://vuex.vuejs.org/guide/getters.html to see how getters are similar to computed properties, with the value of being shared between components. The official Vuex guide will also demonstrate how to initialize your Vue instance with the store.
Here are some snippets to show you the actors in the vuex
system.
// state definition (basically a shared reactive 'data' object that lives outside components)
state:{
message:'Hello'
}
// the store getters are declared as methods and accessed as properties (just like component/computed)
getters:{
message: state => return state.message
}
// component 1 wraps getter
computed:{
message(){
return this.$store.getters.message
}
}
// component 2 also wraps getter
computed:{
message(){
return this.$store.getters.message
}
}
// templates can also use getters directly
<div>{{$store.getters.message}}</div>
// If message was wrapped, you can simply use the computed property
<div>{{message}}</div>
Once you start using vuex
, all sorts of other treasures start to emerge, such as the developer tools in Chrome, undo/redo support, simple refactoring of state, time-travel debugging, app persistence, etc. There are also shortcuts for adding multiple store getters into your computed properties.
As suggested by @Sphinx, you could use a ref
to access the child component.
For example:
<template>
<div id="list">
<product-item v-for="product in sorted" :product="product" :ref="product"></product-item>
</div>
</template>
<script>
import ProductItem from './Product.vue'
export default {
components: { ProductItem },
data: () => ({
hidrated: false,
items: []
})
created() {
this.items = [...] // as noted above
},
mounted() {
this.hidrated = true
},
computed: {
sorted () {
if (!this.hidrated && !Object.keys(this.$refs).length) {
// handle initial state, before rendered
return this.items
}
return Object.values(this.$refs)[0]
.sort((a,b) => b.cost - a.cost)
.map(c => c.product)
}
}
}
</script>
This is assuming you have no other ref
in your List Component
.
You also have to check if the component is rendered first, here I use hidrated
to flag when the component is mounted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With