I started learning Veux. Using the example of creating a card and saving its contents. I created input bound it using two-way binding v-bind - passed it to State. And then nothing is clear. Maybe there are detailed lessons or your examples and help.
<!-- Template
<input id="nameTask" type="text" class="form-control" placeholder="Input Label For New ToDo"
aria-describedby="basic-addon1" v-model="headPrivive">
--!>
<!-- Script
headPrivive () {
return this.$store.getters.headPrivive;
} --!>
<!--Store
import { createStore } from 'vuex'
const store = createStore({
state: {
headPrivive: " ",
},
--!>
Create a variable in data in the component. This will be passed to the store. Set this variable as v-model to the input and later handle setting it in store. Use mutations to update the store states.
<template>
<input
#input="headPrivive()"
id="nameTask"
type="text"
class="form-control"
placeholder="Input Label For New ToDo"
aria-describedby="basic-addon1"
v-model="value"
>
</template>
<script>
export default {
data() {
return {
value: ''
}
}
methods: {
headPrivive () {
// *commit* is the built-in vuex method to call mutations.
// The first parameter is the name of mutation
// The second parameter is the data *value* being passed to the mutation as *payload*
this.$store.commit('updateHeadPrivive', this.value)
}
}
}
</script>
import { createStore } from 'vuex'
const store = createStore({
state: {
headPrivive: " ",
},
mutations: {
// Payload here is the *value* passed from the method *headPrivive* in the component
updateHeadPrivive(state, payload) {
this.headPrivive = payload
}
}
})
Related
I need to modify and update value in mongoDB.
I use a form to do it.
How to pre-fill an Input type text with value ?
You can assign your value from mongoDB to the v-model you are using for input in mounted hook:
<script>
export default {
data() {
return {
message: ''
}
},
methods: {
async getDataFromMongoDB(){
// your existing code here...
return 'Value from DB'
}
},
async mounted() {
this.message = await this.getDataFromMongoDB()
},
}
</script>
<template>
<input v-model="message" />
</template>
Problem
I have some code that is getting a search query from a Vuex store. I am using a computed property to get the search query, and then binding it to the v-model of the input field. I want to be able to edit/change the search term via the input field, and then submit the new search query, which will then perform a new search query.
But the since the computed property is "Read Only", when I change the search query in the input field, it does not update search query, and causes a warning:
vendor.js:16674 Write operation failed: computed value is readonly
Question
How can I get the search query from the Vuex, populate a input field, change/update it, and then submit the changed query? I have tried to find a computed setter for the composition API, but cannot find one.
Any ideas? or should I look at another approach?
Below is the code
<template>
<form role="search"
aria-label="Sitewide"
#submit.prevent="submitSearch"
autocomplete="off">
<input type="text" v-model="searchQuery" />
<button type="button" v-on:click="submitSearch">Search</button>
</form>
</template>
<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
name: "ProductSearchBox",
setup() {
const store = useStore();
const searchQuery = computed(() => store.getters["search/getFiltersSearchTerm"]);
const submitSearch = () => {
store.dispatch('search/performSearch', searchQuery);
}
return {
searchQuery,
submitSearch
}
}
}
</script>
This sounds more like a use case for a watch.
const searchQuery = ref('');
watch(
() => store.getters["search/getFiltersSearchTerm"],
(term) => searchQuery.value = term
);
You can use computed property for v-model like this:
<template>
<form role="search"
aria-label="Sitewide"
#submit.prevent="submitSearch"
autocomplete="off">
<input type="text" v-model="searchQuery" />
</form>
</template>
<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
name: "ProductSearchBox",
setup() {
const store = useStore();
const searchQuery = computed({
get: () => store.getters['search/getFiltersSearchTerm'],
set: (newValue) => store.commit('search/yourMutation', newValue)
});
const submitSearch = () => {
store.dispatch('search/performSearch', searchQuery);
}
return {
searchQuery,
submitSearch
}
}
}
</script>
I'm loading a form component more times in the same page, that's because i have more forms for different tasks, so each form has different parameters.
Html page:
<div id="app">
<myForm formType="buy"></myForm>
<myForm formType="sell"></myForm>
<myForm formType="submit"></myForm>
<refreshAmount></refreshAmount>
</div>
And this is the form component:
<template>
<div>
<div v-if="formType=='buy'">
<form #submit.prevent="formSubmit()">
<input type="text" class="form-control" value="testetete" v-bind:value="amount">
<button v-if="side==='buy'" class="btn btn-primary" style="width: 100%">BUY</button>
<p>Available amount: {{$store.getters.amount}}</p>
</form>
</div>
...
</div>
</template>
<script>
export default {
props:{
formType:{
type:String,
default:'buy'
},
},
mounted() {
console.log('mounted')
},
data() {
return {
amount: this.$store.getters.amount
}
},
methods: {
...
}
}
</script>
Then i have the following store:
<script>
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
amount: 0
},
mutations: {
refreshAmount(state) {
fetch('SOME-URL')
.then(response => response.json())
.then(data => {
state.amount = 100
//state.amount = data['amount']
})
}
},
getters: {
amount: state => state.amount,
}
})
</script>
And finally, the refreshAmount component:
...
mounted() {
this.$store.commit('refreshBalance')
}
...
Basically, i need to show an amount in the form component. This amount is retrieved from my backend, and since i'm loading the form component 3 times, i would call my backend 3 times while i only need to call it once, so i decided to create the component refreshAmount that would call it once and pass it to the form components using a Vuex store.
The problem with my code is the following:
When i load the page, i'll see Available amount: 100 on all the 3 components, so that works; but in the default value of the text input form the value is 0. Why is that? Why isn't the value inside the input text field updated while <p>Available amount: {{$store.getters.amount}}</p> is updated?
Tl;dr: i'm using Vuex to set the value of a variable in my components, when i load the variable between a <p> tag the value is refreshed, while inside the input field the value of the variable stays the same.
amount is not updated because it's a data property, which only gets initialized when the component is first set up and not updated after. What you need is a computed property, which will keep track of the changes in the Vuex store. So instead of:
data() {
return {
amount: this.$store.getters.amount
}
}
you can do:
computed: {
amount() {
return this.$store.getters.amount
}
}
I would like to retrieve all input values from my child components (client and advice, seen below), but not sure how to proceed.
client.vue
<template>
<div id="client">
<input type="text" v-model="client.name" />
<input type="text" v-model="client.code" />
</div>
</template>
<script>
export default {
data() {
return {
client: {
name: '',
code: '',
}
}
}
}
</script>
advice.vue
<template>
<div id="advice">
<input type="text" v-model="advice.foo" />
<input type="text" v-model="advice.bar" />
<div v-for="index in 2" :key="index">
<input type="text" v-model="advice.amount[index]" />
</div>
</div>
</template>
<script>
export default {
data() {
return {
advice: {
foo: '',
bar: '',
amount:[]
}
}
}
}
</script>
Each component has more fields than the above example.
My home page (parent) looks as simple as:
<template>
<form id="app" #submit="printForm">
<clientInfo />
<advice />
<input type="submit" value="Print" class="btn" />
</form>
</template>
<script>
import clientInfo from "#/components/clientInfo.vue";
import advice from "#/components/advice.vue";
export default {
components: {
clientInfo,
advice
},
methods: {
printForm() {}
}
}
</script>
My first idea was to $emit, but not sure how to do that efficiently with more than 20 fields without attaching a #emitMethod="parentEmitMethod" to every single field.
My second idea was to have a Vuex store (as seen below), but I don't know how to save all the states at once and not sure if I should.
new Vuex.Store({
state: {
client: {
name:'',
code:''
},
advice: {
foo:'',
bar:'',
amount:[]
}
}
})
You could use FormData to get the values of the form's <input>s or <textarea>s that have a name attribute (anonymous ones are ignored). This works even if the form has nested Vue components that contain <input>s.
export default {
methods: {
printForm(e) {
const form = e.target
const formData = new FormData(form) // get all named inputs in form
for (const [inputName, value] of formData) {
console.log({ inputName, value })
}
}
}
}
demo
You could use v-model with your custom components. Let's say you want to use them like this:
<advice v-model="adviceData"/>
For this, you would need to watch for value changes on your input elements inside your advice component and then emit an input event with the values. This will update the adviceData binded property. One generic way to do this could be including a watcher inside your advice component, like this:
export default {
data() {
return {
advice: {
foo: '',
bar: '',
amount:[]
}
}
},
watch: {
advice: {
handler: function(newValue, oldValue) {
this.$emit('input', newValue);
},
deep: true,
}
},
}
This way you will not have to add a handler for each input field. The deep option must be included if we need to detect changes on nested data in the advice object.
I think you can achieve what you want is when the user writes something using#change this will trigger a method when the input value is changed, you could use a button instead or anything you want, like this:
The child component
<input type="text" v-model="advice.amount[index]" #change="emitCurrStateToParent ()"/>
You gotta add #change="emitCurrStateToParent ()" in every input you have.
emitCurrStateToParent () {
this.$emit("emitCurrStateToParent", this.advice)
}
Then in you parent component
<child-component v-on:emitCurrStateToParent="reciveDataFromChild ($event)"></child-component>
reciveDataFromChild (recivedData) {
// Do something with the data
}
I would use a button instead of #change, like a "Save" button idk, the same goes for vuex, you can use the #change event
saveDataAdviceInStore () {
this.$store.commit("saveAdvice", this.advice)
}
Then in the store
mutations: {
saveAdvice (state, advice) {
state.advice = advice
}
}
My vue component is like this :
<template>
<div>
<div class="row">
<div class="col-sm-3">
<div class="form-group">
<label for="status" class="sr-only">Status</label>
<select class="form-control" v-model="selected" #change="filterByStatus()">
<option value="">All Status</option>
<option v-for="status in orderStatus" v-bind:value="status.id">{{ status.name }}</option>
</select>
</div>
</div>
</div>
...
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
export default {
...
data() {
return { selected: '' }
},
methods: {
filterByStatus: function() {
this.$store.state.status = this.selected
}
}
}
</script>
My modules order vuex is like this :
import { set } from 'vue'
import order from '../../api/order'
import * as types from '../mutation-types'
const state = {
status: ''
}
const getters = {
...
}
const actions = {
...
}
const mutations = {
...
}
export default {
state,
getters,
actions,
mutations
}
I use this : this.$store.state.order.status = this.selected, to update state when executed, but there exist error like this :
[Vue warn]: Error in callback for watcher "function () { return
this._data.$$state }": (found in )
Error: [vuex] Do not mutate vuex store state outside mutation
handlers.
How can I solve it?
I want update state, because I want the value used by component another
You must have received this error because of enabling strict mode in your vuex store setup.
This, however, is a good practice. You must not modify state except from within a mutation.
So to use the newly setup store; have a mutation in like:
const mutations = {
mutateOrderStatus: function (state, payload) {
state.order.status = payload
}
}
const actions = {
updateOrderStatusAction: function ({commit}, payload) {
commit('mutateOrderStatus', payload)
}
}
Now include it in your component like:
...
methods: {
...mapActions([ // spread operator so that other methods can still be added.
'updateOrderStatusAction'
]),
filterByStatus: function() {
this.updateOrderStatusAction(this.selected)
}
}
Note: you might need babel and babel-preset-es2015 installed to make use of spread operator: ....
I just found a solution of that problem, i used
Vue.set(state.element, elementIndex, newElement);