I want to watch changes of families variable which contains nested objects
<component-test
v-for="family of familiesToDisplay"
// rest
/>
data: () => ({
families: [],
}),
computed: {
familiesToDisplay() {
return this.families.fillter(family => family.members > 4);
},
},
In some response I have seen some recomanding the use of watch but in my case I didn't knew how to implement it since I have never used before.
so the request is to get changes of nested objects in families (as objects I have person and work so if a name of a person has been changed or its work has been changed changes must be retreived here)
This code should work fine, however, it is necessary to correctly mutate objects inside an array. You need to use this.$set or replace the object with a new one to trigger vue re-rendering a list and recalculation of a list.
For example, you can change them like this, (full working example you can find here):
export default {
name: "HelloWorld",
components: {
Display,
},
data: () => ({
families: [
{ name: "foo-1", members: 1 },
{ name: "foo-5", members: 5 },
{ name: "bar-6", members: 6 },
{ name: "bar-3", members: 3 },
],
}),
methods: {
onClick() {
this.families.forEach((family) => {
this.$set(family, "members", this.getRandomNumber());
});
},
getRandomNumber() {
return Math.floor(Math.random() * 10) + 1;
},
},
};
Related
I want to create a search filter. The way it works is a user inputs a text in the search bar, the input is stored using vuex and the result is shown in a different page. Here's an array of objects in a js file
export const productData = [
{
id: 1,
name: "table",
materials: "wood"
},
{
id: 2,
name: "table2",
materials: "metal"
},
{
id: 3,
name: "chair",
materials: "plastic"
}
]
I want to filter using the user's input. Here's my function
import { productData } from '#/data/productData'
export default {
data() {
return {
products: productData
}
},
computed: {
userInput() {
return this.$store.state.userInput
},
filterProducts: function() {
return this.products.filter(q => q.name.match(this.userInput))
}
}
}
When I console the userInput, it works fine! So the problem is in the filterProducts function. It shows an empty array if I console it. What am I doing wrong? Thank you.
edit: the reason I make a new variable called products is because the actual js file is more complex so I had to flatten the array. But the flatten process works fine so I thought I would just simplify the question.
The match function accepts a regex, not String. Give indexOf a try:
filterProducts: function() {
return this.products.filter(q => q.name.indexOf(this.userInput) >= 0)
}
I'm trying to setup a multi select control from bootstrap-vue and bind it to a JSON object. The problem is that I need a computed value to get my json data format in a int array for the multiselect selected values and vice versa. Using such a computed property means that I change date while rendering which leads to an infinite loop.
Currently I created a computed property which has a getter which transforms the JSON object array in a integer array as well as a setter which does the opposite. In my example code the JSON object only contains the id, but in my production code there are a lot of other fields inside a "company".
<template>
<b-form>
<b-form-select
:id="`input-companies`"
v-model="companiesSelected"
multiple
:select-size="4"
:options="availableCompanies"
></b-form-select>
</b-form>
</template>
<script>
const availableCompanies = [
{ value: 1, text: 'company1' },
{ value: 2, text: 'company2' },
{ value: 3, text: 'company3' },
{ value: 4, text: 'company4' }
]
export default {
data () {
return {
employee: { id: 1, name: 'test', companies: [ { id: 1 }, { id: 2 } ] },
availableCompanies: availableCompanies
}
},
computed: {
companiesSelected: {
get () {
if (this.employee.companies == null) {
return []
}
return this.employee.companies.map(company => { return company.id } )
},
set (newValue) {
if (newValue == null) {
this.employee.companies = []
} else {
this.employee.companies = newValue.map(companyId => { return { id: companyId } })
}
}
}
}
}
</script>
The setting of this.employee.companies leads to a infinite loop. I don't really know how to avoid this. Does anyone know how to overcome this issue?
I basically split your computed set into the #change event and it seems to be working.
The #change event should only fire from user interactivity and should therefor cause the loop.
https://codepen.io/Hiws/pen/agVyNG?editors=1010
I'm not sure if that's enough for you, as i didn't take the extra fields on a company into consideration when writing the example above.
In Vue.js, I have a data object with dynamically added/edited properties that are themselves arrays. For example, the property starts out as follows:
data: function () {
return {
vals: {}
};
}
And over time, through various button clicks, etc., vals may look like the following (with the actual property names and values being 100% dynamic based on a number of factors):
vals: {
set1: [
{
prop1: 123,
prop2: 'hello'
},
{
prop1: 456,
prop2: 'bye'
}
],
set2: [
{
prop3: 'Why?!',
prop4: false
}
]
}
As the array properties (i.e., set1 and set2) are changed, I want to be able to react to those changes.
For example, I may do something like the following in my code:
var prop = 'set1';
this.vals[prop].push({
{
prop1: 789,
prop2: 'hmmm...'
}
});
However, when I do that, the component is not updating (I presume because I am pushing an object onto the end of a subarray of an object; and Vue.js doesn't seem to track those changes).
I have been able to force the component to be reactive by doing this.$forceUpdate(); after the above push, but I imagine there has to be a more eloquent way of getting Vue.js to be reactive when it comes to objects being pushed onto the end of object subarrays.
Does anyone know of a better way to try to do what I am trying to achieve? Thank you.
Any time you're adding a new property to an object or changing a value within an array, you need to use Vue.set().
Vue.set(this.vals, prop, [ /* ... */ ])
Ideally, you should define all your properties up front so Vue doesn't have to invalidate computed properties depending on your data model's shape. Even if you have them set to null you should try to map out all the properties you expect your component to need.
Also, in your first code block, you have a colon after your return: which would evaluate to a label, meaning your data function isn't returning anything.
You could try something like this using lodash and a deep watcher..
More on deep watcher here
new Vue({
el: "#app",
methods: {
setValue() {
this.oldPeople = _.cloneDeep(this.people);
}
},
mounted() {
this.setValue();
},
el: "#app",
data: {
changed: null,
people: [
{ id: 0, name: "Bob", age: 27 },
{ id: 1, name: "Frank", age: 32 },
{ id: 2, name: "Joe", age: 38 }
],
oldPeople: []
},
watch: {
people: {
deep: true,
handler(after, before) {
// Return the object that changed
let vm = this;
let changed = after.filter(function(p, idx) {
return Object.keys(p).some(function(prop) {
return p[prop] !== vm.oldPeople[idx][prop];
});
});
vm.setValue();
this.changed = changed;
},
}
}
});
input {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.6/vue.js"></script>
<script src="https://cdn.jsdelivr.net/lodash/4.17.2/lodash.min.js"></script>
<div id="app">
<div>
<input type="text" v-for="(person, index) in people" v-model="people[index].age" />
<div v-if="changed !== null">
You changed:<br/>{{ changed }}
</div>
</div>
</div>
I'm implementing vue-kanban component in my web application. There I'd like to display some objects from my database but I need some help to add them to the kanban board.
This is my array with the projects:
props: {
projects: {
type: Array,
required: true,
}
},
And here I'd like to add them to the kanban board, it should be instead of blocks:
data() {
return {
stages: ['open', 'doing', 'close'],
blocks: [
{
id: 1,
status: 'open',
title: 'test',
},
],
};
}
I use that component: https://github.com/BrockReece/vue-kanban
See What's the correct way to pass props as initial data in Vue.js 2?
If the Kanban component is expecting an attribute like :blocks="[...]" and nothing is going to happen to the data can you not pass the projects array directly to it? e.g :blocks="projects"
If no and the data name blocks is a must and the data needs to be mutable then see below.
export default {
name: "YourComponent",
props: {
projects: {
type: Array,
required: true
}
},
data() {
return {
blocks: this.projects
}
}
}
I recently started learning Vuex and I would like to have some insight on how to properly structure the state of a Vuex/Flux-like stores
Lets take a look at the example below
ProductStore
state: {
name: 'some name',
price: 'some price',
variants: [],
selectedVariant: {},
}
mutations: {
[ADD_VARIANT] (state, newVariant) {
state.variants.push(newVariant)
}
[DELETE_VARIANT] (state, deletedId) {
state.variants = _.filter(state.variants, c => c.id == deleteID )
}
[EDIT_VARIANT] (state, editedComment) {
//...
}
[EDIT_SELECTED_VARIANT_TYPE] (state, variantType) {
state.selectedVariant.type = variantType
}
}
How do you structure states in instances like the one above when you have a parentComponent of sorts(the Product) and you have to manage childComponent states as well(the Variant).
In my specific instance, I have a ProductPage. In it, I have a VariantTable. Selecting an item in the VariantTable brings up a VariantModal that lets you edit variant attributes which should propagate to the parent table.
Normalize your store's state. If Product-Variant relationship is pure 1-n, the store's state can be:
state: {
name: 'some name',
price: 'some price',
variants: [
{ variantId: 'V1', ProductId: 'P1' },
...
],
selectedVariant: {},
products: [
{ productId: 'P1' },
...
]
}
Then with Vuex's action you can add an action to handle update both Variant and Product together:
..., // state goes above
mutations: {
...
[EDIT_PRODUCT] (args) => { ... }
},
actions: {
[EDIT_PRODUCT_VARIANT] ({ commit, state }, editedComment) {
// take extra data if need via state
commit([EDIT_VARIANT], editedComment);
commit([EDIT_PRODUCT], { productId: editedComment.ProductId })
}
}
The point is to avoid data duplication and nested data as much as possible, while allowing data to be updated fast and efficiently.
Read more about data normalization at normalizr