How to use :value and v-model together - vue.js

I need to use :value and v-model together. I've got a form and on the following component I'm receiving an 'age' as a query parameter because I'm filling a previous input somewhere else. I want that age to auto-populate the input on this component. But what if the user changes that value? I'd like to re-store that new age and emit it to the parent .Vue file. How can I do this?
In other words: what if I'm getting a '3' as the age, the auto-populated input shows a '3' but then the user notices they wanted to write '30' instead of '3'? How can I save 30 instead of '3'?
Thanks in advance
<template>
<div>
<p>age</p>
<input type="text" :value="age" v-model="age_keyup" #keyup="send()">
</div>
</template>
<script>
export default {
data: function () {
return {
age_keyup: null,
}
},
methods: {
send(){
this.$emit("age_keyup", this.age);
}
},
props: ['age']
}
</script>

No need to bind the value to that prop just init age_keyup based on age :
<template>
<div>
<p>age</p>
<input type="text" v-model="age_keyup" #keyup="send()">
</div>
</template>
<script>
export default {
data: function () {
return {
age_keyup:null,
}
},
methods: {
send(){
this.$emit("age_keyup", this.age_keyup);
}
},
props: ['age'],
mounted(){
this.age_keyup=this.age
}
}
</script>

Related

How to use v-model and computed properties on Input fields?

I have a simple component with 2 input fields:
<template>
<div>
<input type="text" v-model="name">
<input type="text" v-model="alias">
</div>
</template>
<script>
export default {
data() {
return {
name: "",
alias: ""
}
}
}
</script>
I want to automatically insert the name model's value to the alias field IF the alias field is empty. If it's not empty, I want it to use its own value.
Here is my attempt:
<template>
<div>
<input type="text" v-model="name">
<input type="text" v-model="alias">
</div>
</template>
<script>
export default {
data() {
return {
name: "",
alias: ""
}
},
computed: {
alias: {
get: function() {
if (this.alias.length < 1) {
return this.name
} else {
return this.alias
}
},
set: function(newValue) {
this.alias = newValue
}
}
}
}
</script>
The problem is that alias doesn't get the actual value in the alias property if I type something into the name field. I can only see it visually - but it doesn't hold the value. However, if I type into the alias field, it gets updated properly.
What am I missing here and how can I make it the way I want it?
Computed won't work because it should be treated as immutable.
Also because the model will be updated on each input, a watch won't work either, it would only pick up the first char of what you enter, unless its pre-populated.
This is how I would do it, simply add a #blur event on the name input then fire a method which populates alias if it's empty and on alias in case they empty it out.
The same method could be used in mounted, if you pre-populate the models, or you could watch it.
{
template: `
<div>
<input type="text" v-model="name" #blur="setAlias()">
<input type="text" v-model="alias" #blur="setAlias()">
</div>
`,
data() {
return {
name: '',
alias: ''
}
},
methods: {
setAlias() {
if (this.alias === '') {
this.alias = this.name
}
}
}
}
Firstly, you cannot have a computed property and a data property with the same name. Since both computed and data properties end up as properties on the same state object, one will overwrite the other.
Secondly, and I think you did this because of the first point, in your computed alias getter, your reference the alias again, which is essentially referencing itself, and looks like it could give some inconsistent return values.
I can think of two solutions to your issue:
1) Use a watcher on name:
Create a watcher function for name, and in it set this.alias to the same value as name when alias is blank, or if it's the same as the previous name value.
<script>
export default {
data: () => ({
name: "",
alias: ""
}),
watch: {
name(newVal, oldVal) {
if (!this.alias || this.alias === oldVal) {
this.alias = newVal;
}
}
}
}
</script>
2) Use explicit :value and #change/#keyup bindings on the name input:
v-model is a convenience method that sets both of these for you, but in your case you want to do some more logic in the change handler that just setting a state property value.
<template>
<div>
<input
type="text"
:value="name"
#keyup="onNameInput"
/>
<input type="text" v-model="alias">
</div>
</template>
<script>
export default {
data: () => ({
name: "",
alias: ""
}),
methods: {
// Check and set both values on name input events
onNameInput(e) {
if (!this.alias || this.alias === this.name) {
this.alias = e.target.value;
}
this.name = e.target.value;
}
}
}
</script>

VueJS best practices for passing form-data to child and back to parent

I'm working on a VueJS project and have parent/child components like this.
Parent.vue
<template>
<ChildA v-bind="this.postData.someDataA" />
...
<ChildZ v-bind="this.postData.someDataZ"/>
<button #click="save">Save</button>
</template>
<script>
import ChildA from './ChildA';
...
import ChildZ from '.ChildZ';
data() {
return {
postData: {
someDataA: {field1: 'initialValue'}
someDataB: { // no initial value'}
...
},
},
methods: {
save() {this.$root.db.save(this.postData)}
}
</script>
Child.vue
<template>
<input type="text" v-model="field1" />
...
<input type="text" v-model="field10" />
</template>
<script>
props: {
field1:{type: String, default: 'default if not set by parent'},
...
}
</script>
As you can see, I want to pass this.postData from Parent.vue to a function which saves it to a DB. However, the values for someDataA, etc.. come from the Child.vue.
When I run my code like this, I get the Vue warning:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.
Now, my question is, what's the best practice to deal with this kind of situation? Do I have to implement a <ChildA #change="setSomeDataA()" /> to each of the child elements, and $emit an event each time a value of the childs props change?
Actually it's better to make computed properties instead of using copy of value prop.
props: {
value: {
type: Object,
default: () => ({})
}
},
computed: {
field1: {
get() { return this.value.field1 },
set(field1) { this.$emit('input', {...this.value, field1 })}
}
}
and use it like that
<input type="text" v-model="field1" />
Set up your child to use v-model and then emit the change. For example:
The parent calls the child using v-model
Parent.vue
<template>
<ChildA v-model="this.postData.someDataA" />
...
<ChildZ v-model="this.postData.someDataZ"/>
<button #click="save">Save</button>
</template>
<script>
import ChildA from './ChildA';
...
import ChildZ from '.ChildZ';
data() {
return {
postData: {
someDataA: {field1: 'initialValue'}
someDataB: { // no initial value'}
...
},
},
methods: {
save() {this.$root.db.save(this.postData)}
}
</script>
The child takes the prop value and sets an internal variable to value. Your input uses the internal variable as it's v-model. Then using #change or a watcher to emit('input', <your internal variable>)
Child.vue
<template>
<input type="text" #change="objChanged" v-model="myObj.field1" />
...
<input type="text" #change="objChanged" v-model="myObj.field10" />
</template>
<script>
props: {
value:{type: String, default: 'wont need a default'},
...
},
data: {
myObj: {}
},
methods: {
objChanged(){
this.$emit('input', this.myObj);
}
},
created(){
this.myObj = this.value;
}
</script>

How to get component data? How to access the components from the outside?

I derive the component:
<my-component> </my-component>
The question is how to get the properties of this particular component, knowing only its tag "my-component"?
A more detailed description of the problem:
Using the vue-cli-service build --target wc --name my-component my-component.vue command, I compile in js file
Then I connect this script in the header of the site.
Then I use the component
But how to get the properties of this component now and generally how to access this component? How to get the input field value in this component?
Sorry, I probably didn’t explain it well.
I need something like this:
<my-component id="my-component"> </my-component>
<script>
let inputValue = $("#my-component").find("input").val();//but this not work
</script>
Or
<script>
let props = <my-component></my-component>// this component props
</script>
In the my-component.vue file, this code:
<template>
<input :placeholder="test" type="text" name="test" value="">
</template>
<script>
export default {
name: 'MyInputComponent',
props: {
placeholder: {
type: String,
default: "00"
}
},
data () {
return {
}
},
}
</script>
usage
<template>
<input :placeholder="placeholder" type="text" #input="emitInputValue">
</template>
<script>
export default {
name: 'MyInputComponent',
props: {
placeholder: {
type: String,
default: "00"
}
},
methods: {
emitInputValue ($event) {
this.$emit('input', $event.target.value)
}
}
}
</script>
get value
<my-component #input"getValue"> </my-component>
methods: {
getValue (payload) {
console.log(payload)
}
}
<template>
<input :placeholder="test" :id="id" type="text" name="test" value="">
</template>
<script>
export default {
name: 'MyInputComponent',
props: {
placeholder: {
type: String,
default: "00"
},
id: {
type: String
}
},
data () {
return {
}
},
}
</script>
Now your id would be available and you can do whatever you want.
Only one suggestion i would like to give is: when you are using vue.js then try avoiding jquery for getting input value. there are many ways in vue.js itself you can get input text value.
Let me know if you need something else

Set form action dynamically using computed property

I'm trying to send form to certain action, based on select value.
I have such template:
<template>
<form method="post" :action="myRoute" ref="myForm">
<select #change="entitySelected" v-model="selected">
<!-- -->
</select>
</form>
</template>
I'm trying to set up form action dynamically when new select value is appeared:
<script>
export default {
data() {
return {
selected: '',
}
},
computed: {
myRoute: function () {
return 'example.com/'+this.selected
}
},
methods: {
entitySelected(event) {
console.log(this.$refs.myForm.action) //<- action is 'example.com' without selected value
console.log(this.selected) //<- this value is as expected
this.$refs.myForm.submit()
}
}
}
</script>
What's wrong?
P. S. Browser - Firefox
Probably not the best way, but it works:
userSelected(event) {
this.$nextTick(function () {
this.$refs.backdoor.submit()
})
}
You can use setAttribute() when updating the selected value :
this.$refs.myForm.setAttribute('action', this.myRoute);

Vuex - Computed property "name" was assigned to but it has no setter

I have a component with some form validation. It is a multi step checkout form. The code below is for the first step. I'd like to validate that the user entered some text, store their name in the global state and then send then to the next step. I am using vee-validate and vuex
<template>
<div>
<div class='field'>
<label class='label' for='name'>Name</label>
<div class="control has-icons-right">
<input name="name" v-model="name" v-validate="'required|alpha'" :class="{'input': true, 'is-danger': errors.has('name') }" type="text" placeholder="First and Last">
<span class="icon is-small is-right" v-if="errors.has('name')">
<i class="fa fa-warning"></i>
</span>
</div>
<p class="help is-danger" v-show="errors.has('name')">{{ errors.first('name') }}</p>
</div>
<div class="field pull-right">
<button class="button is-medium is-primary" type="submit" #click.prevent="nextStep">Next Step</button>
</div>
</div>
</template>
<script>
export default {
methods: {
nextStep(){
var self = this;
// from baianat/vee-validate
this.$validator.validateAll().then((result) => {
if (result) {
this.$store.dispatch('addContactInfoForOrder', self);
this.$store.dispatch('goToNextStep');
return;
}
});
}
},
computed: {
name: function(){
return this.$store.state.name;
}
}
}
</script>
I have a store for handling order state and recording the name. Ultimately I would like to send all of the info from multi step form to the server.
export default {
state: {
name: '',
},
mutations: {
UPDATE_ORDER_CONTACT(state, payload){
state.name = payload.name;
}
},
actions: {
addContactInfoForOrder({commit}, payload) {
commit('UPDATE_ORDER_CONTACT', payload);
}
}
}
When I run this code I get an error that Computed property "name" was assigned to but it has no setter.
How do I bind the value from the name field to the global state? I would like this to be persistent so that even if a user goes back a step (after clicking "Next Step") they will see the name they entered on this step
If you're going to v-model a computed, it needs a setter. Whatever you want it to do with the updated value (probably write it to the $store, considering that's what your getter pulls it from) you do in the setter.
If writing it back to the store happens via form submission, you don't want to v-model, you just want to set :value.
If you want to have an intermediate state, where it's saved somewhere but doesn't overwrite the source in the $store until form submission, you'll need to create such a data item.
It should be like this.
In your Component
computed: {
...mapGetters({
nameFromStore: 'name'
}),
name: {
get(){
return this.nameFromStore
},
set(newName){
return newName
}
}
}
In your store
export const store = new Vuex.Store({
state:{
name : "Stackoverflow"
},
getters: {
name: (state) => {
return state.name;
}
}
}
For me it was changing.
this.name = response.data;
To what computed returns so;
this.$store.state.name = response.data;
I've had such an error when getting value from the store, in computed, via ...mapState(['sampleVariable']), as you. Then I've used the this.sampleVariable in <script> and sampleVariable in <template>.
What fixed the issue was to return this in data(), assign it to a separated variable, and reuse across the component the newly created variable, like so:
data() {
return {
newVariable: this.$store.state.sampleVariable,
}
}
Then, I've changed references in the component from sampleVariable to newVariable, and the error was gone.
I was facing exact same error
Computed property "callRingtatus" was assigned to but it has no setter
here is a sample code according to my scenario
computed: {
callRingtatus(){
return this.$store.getters['chat/callState']===2
}
}
I change the above code into the following way
computed: {
callRingtatus(){
return this.$store.state.chat.callState===2
}
}
fetch values from vuex store state instead of getters inside the computed hook