Vuex state not update without reload - vue.js

<template>
<ContactField
v-for="(field, $fieldIndex) in contact.fields"
:key="$fieldIndex"
:fieldIndex="$fieldIndex"
:contact="contact"
:fieldName="field.fieldName"
v-model="field.fieldValue"
/>
<div>
Add field
<input type="text" v-model="newFieldName" />
<input type="text" v-model="newFieldValue" />
<button #click="addFieldToContact">Add</button>
</div>
<div>
<button #click="saveChanges">Save</button>
</div>
</div>
</template>
export default {
data() {
return {
newFieldName: '',
newFieldValue: '',
}
},
components: {
ContactField
},
computed: {
id() {
return this.$route.params.id
},
...mapGetters(['getContact']),
contact() {
return this.getContact(this.id)
}
},
methods: {
addFieldToContact() {
this.$store.commit('ADD_FIELD', {
contact: this.contact,
fieldName: this.newFieldName,
fieldValue: this.newFieldValue
})
this.newFieldName = ''
this.newFieldValue = ''
}
}
}
Vuex store
const contacts = ...
export default new Vuex.Store({
state: {
contacts
},
mutations: {
ADD_FIELD(state, { contact, fieldName, fieldValue }) {
contact.fields.push({
fieldName: fieldName,
fieldValue: fieldValue
})
}
},
getters: {
getContact: state => id => {
for (const contact of state.contacts) {
if (contact.id == id) {
return contact
}
}
}
}
})
When i click button "add" i can see what fields created on page but not in state(state hasn't this field's that i add just now) but if i refresh page state add to yourself this fields.
Question: is this correct? Or it depends on the situation? Do I need to update state directly?
Some code i delete because i cant ask when i use a lot of code.

Your contacts are not changed trough the state. You are pushing your new object to the variable passed to the ADD_FIELD method.
Maybe you can try to find and replace the contact in your contacts array. Or if it is new one just push it to contacts. This should happen at the end of your ADD_FIELD(...) method.

Related

Get object from Vuex at specific index

I am getting an array from Vuex and I want an object at 2 positions.
HTML
<p class="">
{{ MainImg[2].para}}
</p>
Vue
export default {
name: "App",
components: { },
data() {
return {
imageQuery: this.$route.params.image,
};
},
computed: {
...mapGetters("design", {
MainImg: ["singleDesigns"]
})
},
created() {
this.fetchDesigns();
},
mounted() {
console.log(this.MainImg);
},
methods: {
fetchDesigns() {
this.$store.dispatch("design/getSingleDesign", this.imageQuery);
}
}
};
But it shows an undefined error.
And When I add MainImg array in Vue data like this.
data() {
return {
imageQuery: this.$route.params.image,
MainImg:[{para:"1"},{para:"2"},{para:"3"},{para:"4"}]
};
It Works.
P.S.-
Store Code-
export const state = () => ({
designs: [],
})
export const getters = {
singleDesigns(state) {
return state.designs;
}
}
I am not adding Action and Mutation because it works fine with other code.
It looks like the array is empty at the first rendering, so you should add a condition to render it :
<p class="" v-if="MainImg && MainImg.length >= 2">
{{ MainImg[2].para}}
</p>

Dynamic component by select input /w Vuex store binding

I don't know how to change a dynamic component based on a selectbox option with two-way binding to a Vuex Store.
My single file component (Fieldvalue) houses this dynamic component and also the selectbox.
Its type value comes from a Vuex Store, where it can be found by the fieldId, which is passed through the parent component.
So to a field with an ID like 41, there is a fieldvalue component.
The get() function of fieldtypeComponent has somehow to know the passed through fieldId value from the props, that the parent passes down, to have the ID for searching in the fieldvalues array of the store.
But it does not work how I'm doing it. (commented out lines)
Any ideas?
My Vuex store getter:
export function getFieldvalueTypeByFieldnameId(state){
return (fieldId) => {
const fieldvalue = state.form.fieldvalues.find(e => e.fieldId == fieldId);
return fieldvalue.type;
}
}
Mutation:
export function setFieldvalueType(state, obj){
const { type, fieldId } = obj;
const fieldvalue = state.form.fieldvalues.find( e => e.fieldId === fieldId );
fieldvalue.type = type;
}
My Vuex store state example:
{
"form": {
"name": "",
"fieldvalues": [
{
"formularId": null,
"fieldId": 41,
"value": {},
"type": ""
},
{
"formularId": null,
"fieldId": 44,
"value": {},
"type": ""
}
]
}
}
My single file component:
<template>
<div class="row">
<div class="col-3 self-center">
<q-select
square
filled
v-model="fieldtypeComponent"
:options="fieldtypes"
option-value="component"
map-options
emit-value
label="Type"
/>
</div>
<div class="col">
<template
v-if="fieldtypeComponent === null || fieldtypeComponent == undefined">
<p>no type selected</p>
</template>
<component v-else :is="fieldtypeComponent"></component>
</div>
</div>
</template>
<script>
import IconField from "components/fieldtypes/IconField";
import TextField from "components/fieldtypes/TextField";
export default {
name: "Fieldvalue",
data() {
return {
selected: "",
fieldtypes: [
{ label: "Icon", component: "IconField" },
{ label: "Text", component: "TextField" }
]
};
},
components: {
IconField,
TextField
},
computed: {
fieldtype: function() {
return this.$store.getters["formular/getFieldvalueTypeByFieldnameId"];
},
fieldtypeComponent: {
get() {
//return this.fieldtyp(this.fieldnameId);
//return this.$store.getters["formular/getFieldvalueType"];
return null;
},
set(type) {
const payload = {
type,
fieldId: this.forFieldnameId
};
this.$store.commit("formular/setFieldvalueType", payload);
}
}
},
props: {
forFieldnameId: { type: Number, required: true }
},
methods: {
changeComponent() {
this.fieldtypeComponent = this.selected;
const payload = {
type: this.selected,
fieldId: this.forFieldnameId
};
this.$store.commit("tarife/setFieldvalueTyp", payload);
}
}
};
</script>
Additional info on what I want to do:
I have some fields defined in a formular with a name. To each fieldname from that form I want to save a value later.
But that value (a javascript object/JSON) is different depending on the choosen component from the select input. (TextField or IconField)
I don't know if my way of passing down is right or too complex. Especially regarding to two-way binding or just emitting up to the parent component and let it handle the complete logic.
I need to somehow bind the value from in the chosen component to the Vuex store fieldvalue entry too.
Maybe my approach is wrong and I should do it totally different?

Editing tasks in vue

cannot edit list
I think the problem is in the update mutation.
Everything is fine with the label, probably a problem with input, it does not update the data.
I need to make the tasks can be edited on a double wedge.
cannot edit list
I think the problem is in the update mutation.
Everything is fine with the label, probably a problem with input, it does not update the data.
I need to make the tasks can be edited on a double wedge.
Vue.use(Vuex)
export default new Vuex.Store({
state: {
todos: localData().get()
},
mutations: {
editTodo: (state, id) => {
let todo = state.todos.find(todo =>
(todo.id === id))
todo.edit = true
localData().set(state.todos)
}, //mutations editTodo
update: (state, id, newEvent) => {
let todo = state.todos.find(todo =>
(todo.id === id))
todo.title = newEvent
todo.edit = false
localData().set(state.todos)
},
},
})
<template>
<li>
<label
v-if="!edit"
#dblclick="editTodo"
>
{{ title }}
</label>
<input
v-else
class="edit"
type="text"
:value="newEvent" //it seems he is interrupting the title
#keyup.enter="update"
>
</li>
</template>
<script>
export default {
name: 'todo',
props: ['id', 'title', 'edit', 'completed'],
data() {
return {
newEvent: '' //Am I doing the right thing to add newEvent?
}
},
computed: {
todos() {
return this.$store.state.todos
}
},
methods: {
editTodo() {
this.$store.commit('editTodo', this.id)
},
update() {
this.$store.commit('update', this.id, this.newEvent) //update method
},
}
}
First, let's define what is wrong in your code. You're updating Vuex state object using update function but you're giving :value="newEvent" which is in your component, so Vuex doesn't see this. First, create state data and getters for newEvent
state:{
//..
newEvent: ""
}
getters:{
newEvent: state => state.newEvent
}
Then use this state element in your component
// ..
import { mapGetters } from "vuex"
// ..
computed:{
...mapGetters(["newEvent"])
}
You should use logic like that

When removing images the last image removed stays (no re-render)

I have an array of objects, each has a URL that is being loaded in this file:
<template>
<div>
<img :key="id" :src="img" alt="" class="image box" #click="cardClicked" />
</div>
</template>
<script lang="ts">
export default {
props: ["id", "value", "type", "owner", "imgURL"],
data() {
return {
img: require(`./../assets/${this.imgURL}.png`)
};
},
methods: {
cardClicked() {
this.$store.commit("addCardToPlayer", {
id: this.id,
type: this.type,
value: this.value,
owner: this.owner
});
}
}
};
</script>
In the Store mutation I preform filtering and while filtering I add the card to a another player, like so:
addCardToPlayer(state, clickedCard) {
const owner = clickedCard.owner;
const type = clickedCard.type;
const currPlayer = state.currentPlayerName;
if (clickedCard.owner === "deck") {
state.cardOwners[owner].cards[type] = state.cardOwners[owner].cards[
type
].filter(card => {
if (card.id === clickedCard.id) {
state.cardOwners[currPlayer].cards[type].push(card);
return false;
} else return true;
});
}
},
When clicking a card to remove, I see the card being added to the player and displayed correctly, and the number of cards displayed after removal is correct.
But
The card that was removed still shows.
What have I tried:
Forcing to re-render using:
cardClicked() {
this.$store.commit("addCardToPlayer", {
id: this.id,
type: this.type,
value: this.value,
owner: this.owner
});
this.$forceUpdate();
}
Making different components have key, and trying to change the key to cause a re-render:
data() {
return {
componentKey: 0,
};
},
methods: {
forceRerender() {
this.componentKey += 1;
}
}
Tried changing how I change the array by creating a new state.
EDIT: Tried using computed. i get an error:
TypeError: Cannot read property 'imgURL' of undefined
computed: {
getImg: () => {
return require(`./../assets/${this.imgURL}.png`);
}}
EDIT: I wrote the computed function as arrow function, which doesn't preserve context.
SOLVED, by using
computed: {
getImg() {
return require(`./../assets/${this.imgURL}.png`);
}}
How can I make the images update after click (remove).
Thanks.
SOLVED, by using
computed: {
getImg() {
return require(`./../assets/${this.imgURL}.png`);
}}

VueJs getting child form data from parent component

I have a form in my child component:
<form #submit="submitForm">
<input type="text" v-model="textInput" />
</form>
export default {
name: "childComp",
data: function() {
return {
textInput: ""
}
}
}
Now from my parent component I have a button and I need to click on that button to get the child form data.
<button type="button" #click="getFormData()"> click </button>
export default {
name: "ParentComp",
data: function() {
return {
formData: []
}
},
methods: {
getFormData(d) {
formData.push(d);
console.log(d)
}
}
}
Appreciate your help.
Even though you got it solved by using $ref, i would recommend utilizing the power of a v-model implementation into your custom component.
This is a more clean approach, and by doing this, you'll always have the form data at hand, instead of having to actually retrieve it when you want to use it.
It can be done by doing the following:
Parent
<button type="button" #click="getFormData()"> click </button>
<childComp v-model="formData" />
export default {
name: "ParentComp",
data: function() {
return {
formData: {}
}
},
methods: {
getFormData() {
console.log(this.formData)
}
}
}
Child
<form>
<input type="text" v-model="selected.text" />
</form>
export default {
name: "childComp",
props: ['value'],
computed: {
selected: {
get() {
return this.value;
},
set(value) {
this.$emit('input', value);
}
}
}
}
I found the solutions using ref="". Not sure how complex it will get in future.
Here is what I did.
My parent component:
<button type="button" #click="getFormData()"> click </button>
<childComp ref="childComp" />
export default {
name: "ParentComp",
data: function() {
return {
formData: []
}
},
methods: {
getFormData() {
const data = this.$refs.childComp.submitForm()
formData.push(data);
console.log(data)
}
}
}
My child component:
<form #submit="submitForm">
<input type="text" v-model="textInput" />
</form>
export default {
name: "childComp",
data: function() {
return {
textInput: ""
}
},
submitForm() {
return this.form;
}
}