vue2 component: force update computed propriety using methods - vuejs2

i'm trying to update a computed propriety using a method inside a component,
here the example:
props: ['selezionati'],
computed:{
listaSelezionati() {
return this.selezionati
}
},
methods:{
rimuoviSotto : function(index,indexparent){
var obj = JSON.stringify(this.listaSelezionati[0][indexparent].sottoservizio[index]);
alert(obj);
var mod = this.listaSelezionati[0][indexparent].sottoservizio.splice(index,1);
vue.set(this.listaSelezionati,mod);
}
}
basically i want to splice a sub array nested data,
the obj var is only to debug, and trigger the correct value, i have tried to apply the array splice without var, it seems to work, but don't apply the modify to component view, so i was trying to use the vue.set but the console return me "is not a function".
basically what can i do to update the computed propriety to the view?
thank you

i solved using $forceUpdate();
basically in the method i must force the computed propriety

computed properties are dependent properties . They update when their dependent data properties which are reactive update.
So assign the vaue of the prop to a data property
props: ['selezionati'],
data((){
return{
listaSelezionati: this.selezionati
}
},
methods:{
rimuoviSotto : function(index,indexparent){
var obj = JSON.stringify(this.listaSelezionati[0][indexparent].sottoservizio[index]);
alert(obj);
var mod = this.listaSelezionati[0][indexparent].sottoservizio.splice(index,1);
this.listaSelezionati = mod;
}
}
If you want to update the prop that you are reviving from the parent , then you should use events as props are one-way data flow

Related

VueJS: How to to access parent function in child to conditionally render css classes?

I have a parent component with a function like (simplified example)
isValid(value) { return value > validationModifier }
Both the parent and the child use that function to conditionally render e.g. CSS classes. So in my child I would like to use:
:class="{'my-class' : isValid(myValue)}"
But I don't have access to this function. I want to avoid duplicating it in the child, and I don't see how emitting an event would work in this case.
What is the appropriate way to deal with this?
If the function has reusable logic, rather than specific to that parent component, then I would use a mixin. If you want to add any other shared logic (methods, computed functions) you can edit the mixin and don't have to explicitly add the new parameter to parent and child
mixin code:
const myMixin = {
methods:{
isValid(param1){
return param1 < validationModifier
}
}
}
then to inject into any of your components
{
name: "my-custom-component",
mixins:[myMixin],
methods:{}
}
You can pass the function to the child like a classical function prop https://v2.vuejs.org/v2/guide/components-props.html#Prop-Types
No need to use the event/emit system here.
<child v-bind:is-valid="isValid"></child>
#Joel H's answer is one of the ways to reuse functions in Vue. Another way is to use dependency injection in Vue. See https://v2.vuejs.org/v2/guide/components-edge-cases.html#Dependency-Injection
You just have to provide the method and all the children components of the ParentComponent can access that isValid method. Dependency injection in Vue is not limited to functions only, you can pass variables and data too.
export default {
name: 'ParentComponent',
...
methods: {
isValid(value) { return value > validationModifier },
},
provide() {
return {
isValid: this.isValid
}
}
}
and in your ChildComponent ...
export default {
name: 'ChildComponent',
...
inject: ['isValid']
}
Now you can use the function in your ChildComponent using this.isValid(yourValueHere).

Computed function running without to call it

I'm setting an array in my data property through a computed function and it's working. But I wonder how is possible if I don't call it anywhere?
If I try to add a console.log in my function it doesn't print anything, but it's still setting my data, how is that possible?
My data:
data() {
return {
projects: []
};
},
My computed:
computed: {
loadedProjects() {
console.log("Hello there")
this.projects = this.$store.getters.loadedProjects
}
},
I expect that it doesn't run because I'm not calling, and if it is running(I don't know why) to print the console.log before to set my data. Any clarification?
Thanks:)
You're confusing computed props with methods. If you want to have a method like above that sets a data value of your vue instace, you should use a method, not a computed prop:
data() {
return {
projects: []
};
},
methods: {
loadProjects() {
console.log("Hello there")
this.projects = this.$store.getters.loadedProjects
}
}
This would get the value of this.$store.getters.loadedProjects once and assign it to your local projects value. Now since you're using Vuex, you probably want your local reference to stay in sync with updates you do to the store value. This is where computed props come in handy. You actually won't need the projects in data at all. All you need is the computed prop:
computed: {
projects() {
return this.$store.getters.loadedProjects
}
},
Now vue will update your local reference to projects whenever the store updates. Then you can use it just like a normal value in your template. For example
<template>
<div v-for='item in projects' :key='item.uuid'>
{{item.name}}
</div>
</template>
Avoid side effects in your computed properties, e.g. assigning values directly, computed values should always return a value themselves. This could be applying a filter to your existing data e.g.
computed: {
completedProjects() {
return this.$store.getters.loadedProjects.filter(x => x.projectCompleted)
},
projectIds() {
return this.$store.getters.loadedProjects.map(x => x.uuid)
}
}
You get the idea..
More about best practices to bring vuex state to your components here: https://vuex.vuejs.org/guide/state.html
Computed props docs:
https://v2.vuejs.org/v2/guide/computed.html
You should check Vue docs about computed properties and methods
and shouldn't run methods inside computed property getter
https://v2.vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods
Instead of a computed property, we can define the same function as a method. For the end result, the two approaches are indeed exactly the same. However, the difference is that computed properties are cached based on their reactive dependencies. A computed property will only re-evaluate when some of its reactive dependencies have changed.

How to automatically construct watch property based on data attributes in Vue.js?

I have standard Vue.js component and I'd like to convert attributes in data property to watcher or in other words I want to construct a watch object based on data property attributes automatically
my idea looks something like this
watch: {
...(() => {
const watchers = {}
Object.keys(this.$data).forEach(key => {
watchers[key] = () => {
new ProductNutrientUpdate(this).run()
}
})
return watchers
})(),
},
the problem with this approach is that this.$data is not constructed yet
maybe there is some way how I can add watchers in created hook for example??
Vue already watches properties of the data object (note if any of these values are themselves objects, I think you need to update the whole object, i.e. change its value to a shallow copy with the desired nested key-values).
Refer to: https://v2.vuejs.org/v2/guide/reactivity.html
You can then use the update lifecycle hook to watch for all changes to data: https://v2.vuejs.org/v2/api/#updated
I was able to resolve a challenge using the following approach
created() {
Object.keys(this.$data).forEach(key => {
this.$watch(key, function() {
// ... some logic to trigger on attribute change
})
})
}

How can I clone data from Vuex state to local data?

How can I clone data from vuex state to local data attribute?
State
this.tsStore.shemes
Data Attribute
data () {
return { shemes: [] }
}
I've tried do this in updated () this.shemes = this.tsStore.shemes but it's seems like it has a binding left.. because when i delete one item in this.shemes on click i've also delete that item in the state and get the error of "Do not mutate vuex store state outside mutation handlers".
I need to clone the state and do what ever I need to do with that data and on the same time don't affect my state state.
Try
this.shemes = JSON.parse ( JSON.stringify ( this.tsStore.shemes) )
This will clone all value and objects from the array in the store.
You need to create a new array. this.tsStore.shemes give you a reference to the bound array.
You can try to use the spread operator or arr.slice() to create a new array with the same content.
notice that this is a shallow copy.
this.shemes = [...this.tsStore.shemes]
or
this.shemes = this.tsStore.shemes.slice()
Using cloneDeep is still the best way to go, here is an example
<script>
import { cloneDeep } from 'lodash-es'
...
const properlyClonedObject = cloneDeep(myDeeplyNestedObject)
...
</script>
It's bullet proof, battle-tested and is also a tree-shakable function.
If you need this for Nuxt, here is how to achieve this.
data(){
return {
shemes: null,
}
},
beforeMount() {
this.shemes = this.stateShemes
},
computed: {
stateShemes() { return this.tsState.shemes }
// OR that's how I do
stateShemes() { return this.$store.getters['shemes'] }
}
UPDATE
So you get some value from your state by using computed variables. You cannot just assign the value from you store in the data() block. So you should do it beforeMount. That way if you have a watcher for shemes variable, it won't trigger on assigning computed value. If you put it in mounted() hook, the watcher will trigger.
Also, can you explain why do you use this call this.tsState.shemes instead of this.$store.getters.shemes?

vuejs pass model from parent component to children component and allow mutation

Currently I have a vue-multiselect component which requires a v-model.
I want to wrap this component so that I can build one single-select component and one multi-select component.
While working on the single select component I encountered the following warning
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "model"
They are right but in my case I really need to change the value from the parent (like I replace my single-select code with the vue-multiselect code) component and I also do not want this warning.
Here is the code for my component:
Vue.component('single-select', {
props: {
model: {
required: true
}
}
template: '<multiselect\n' +
' v-model="model"\n' +
...>\n' +
...
'</multiselect>'
});
One solution would be to pass a function as a model parameter and return the field from the parent but I really hope for a better solution.
Vue has a shortcut for 2 way binding called .sync modifier.
How it works in your case:
add .sync when you pass model as prop
<single-select :model.sync="..."></single-select>
emit an update:model in the child's input event
Vue.component('single-select', {
props: {
model: {
required: true
}
},
template: `<multiselect :value="model" #input="$emit('update:model', $event)"> </multiselect>`
});
Just give the internal model reference a different name, and the in the Vue component's data function map it manually:
Vue.component('single-select', {
props: {
model: {
required: true
}
},
data: function() {
return {
singleSelectModel: this.model
};
}
template: '<multiselect v-model="singleSelectModel"></multiselect>';
});
This is of course, assuming that you do not want to mutate the parent data, but simply making a copy of model and giving the child component the freedom to change it whenever it wants.
If what you want is to also update the parent data from the child, you will have to look into emitting events from the child and listening in the parent.