How does one make arrow keys on number inputs lazy in Vue? - vue.js

Vue supports lazy binding to models with the lazy modifier, e.g.
<input v-model.lazy="value" />
Now the model isn't updated until the input loses focus. However, if I change the type to Number and use the arrow keys to set the value, the model updates while the input has focus:
<input type="number" v-model.lazy="value" />
Is there an (easy) way to delay binding until after focus is lost?

v-model is synonymous for :value + #change. Assuming the arrows on input trigger a focus event, you can try replacing v-model with :value and #blur pair. Might not work if .lazy modifier already does this.
<input type="number" :value="value" #blur="value = $event.target.value" />
Another alternative is to "debounce" the change event with a set time so the value doesn't update while the user is changing the value.
Edit: debounce example using npm package
After installing and importing the debounce package, you need to create/assign the "debounced" version of the method (should define it in methods) to a method name under created (can be a different method name but should match what you put in #blur listener.
<input type="number" :value="value" #blur="updateValueOnBlur" />
created() {
this.updateValueOnBlur = debounce(this.updateValueOnBlur, 500);
},
methods: {
updateValueOnBlur(e) {
this.value = e.target.value;
},
}

Related

vue: react-like controlled checkbox?

in React, we can have a controlled checkbox like this
<input type="checkbox" checked={true} />
and this will keep the checkbox always checked, even the users click it
but in Vue, after binding a true value for checked
<input type="checkbox" :checked="true" />
it's initially checked, but the users can still click to change it.
how to prevent this behavior in Vue?
update:
what I'm really trying to implement is
a checkbox can click to uncheck
but can not click to check, to only way to make it checked is by changing the <select /> next to it
uncheck the checkbox will clear <select />
my current implementation: https://codesandbox.io/s/goofy-mcnulty-seh6w?file=/src/App.vue
using key and #click.prevent kind of does not fit my head.
I've come up with following solution for your problem.
Lets go step by step and explain how it works.
We set the input element's checked property to be a local boolean value (checkboxChecked). Also we want it to be automatically changed once the checkbox is clicked, which is why we also add this checkboxChecked boolean as v-model attribute.
We prevent the user from checking it again by setting the disabled attribute of the input element to be true if the checkbox is not checked (checkboxChecked = false).
On the select element we listen for the #change event (See this question on the change event) and run the onSelectChange method. This method then sets the checkboxChecked boolean back to true, enabling the user to uncheck the checkbox again.
Furthermore, we add a ref attribute to the first option (The one we want to select if the checkbox gets unchecked).
Lastly we add a watcher (Check out the docs on value watchers) to the checkboxChecked attribute. If the checkbox is now clicked the if statement in our watcher function selects the first option using the ref attribute if the checkboxChecked value is false.
Edit
What I forgot to mention is, that you would also have to check if the user actually selects an other option than empty (I've done this by comparing the event.target.value to an empty string). I've added this to the code now.
Also a client still could go ahead and manually set the disabled attribute with his webbrowsers html editor to false.
<template>
<div id="app">
<input
type="checkbox"
:checked="checkboxChecked"
:disabled="!checkboxChecked"
v-model="checkboxChecked"
/>
<select style="width: 5em" #change="onSelectChange($event)">
<option ref="emptySelectOption" value="">empty</option>
<option value="1">1</option>
<option value="2">2</option>
</select>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
checkboxChecked: true,
};
},
methods: {
onSelectChange(event) {
if (event.target.value !== "") this.checkboxChecked = true;
},
},
watch: {
checkboxChecked(value) {
if (!value) this.$refs.emptySelectOption.selected = true;
},
},
};
</script>

Vuex v-model with an array

So I know to use v-model with vuex you use a computed property with a setter and getter, where the getter dispatches an action. How would this work when the property you want to bind with is an array? This is how the the code worked pre-Vuex so trying to convert it since newAds now is within Store.
<div v-for="ad in newAds" :key="ad.id">
<div v-for="key in ad" :key="key.id">
<input
type="text"
v-model="key.finalurl"
placeholder="www.books.com"
autofocus
>
</div>
</div>
Can I pass multiple parameters through a setter and then run that through to the mutation?
You can split v-model into the #input event handler and the :value property, and then handle multiple values, like this:
<input
type="text"
:value="key.finalurl"
#input="handleFinalURL"
placeholder="www.books.com"
autofocus
>
methods: {
handleFinalURL(value) {
// do stuff here like
callFunction(value, someOtherParam, extra)
}
}
It's less handy but it will solve your problem.

What is the right way to use :value and v-model together in a <input>

this is my wrong code:
<input v-model="input.nameInput" type="text" :value="name" autocomplete="off" class="form-control">
<input v-model="input.posInput" type="text" :value="pos" autocomplete="off" class="form-control">
i can display the {{name}} and the {{pos}} outer of and its work. but if v-model and :value merged, error:
v-bind:value="name" conflicts with v-model on the same element because the latter already expands to a value binding internally
so what is the correct way? thanks
value is the same as v-model, v-model however updates on input where value does not.
There have been a few times I've used value over v-model but if you do this you would have to watch on input and change and update the value using a function instead.
do the following for this.
<input v-model="input.nameInput" type="text" autocomplete="off" class="form-control">
<input v-model="input.posInput" type="text" autocomplete="off" class="form-control">
Remove the value and keep the v-model.
You shouldn't use both v-bind and v-model on the same element. Consider the following code:
<label>
Input with v-bind
<input v-bind:value="message" />
</label>
<label>
Input with v-model
<input v-model="message" />
</label>
<p>{{ message }}</p>
The input with v-bind:value="message" (or it's shorthand, :value="message" will do the exact same), implements one-way binding. Any changes done to the "message" variable will be updated in this input, but changes done in the v-bind input will not change the message variable.
The input with v-model="message" has two-way binding. This means that changes to the input will reflect the message variable, but changes to the message variable will also reflect on the input. If you're building a form in vue, this type of binding is usually the way to go.
I made a JSFiddle where you can test both types of binding.
If your intention with both a v-bind and a v-model was to set a default value to your input field, you can set this value in the component's data.
data: {
message: 'My default value',
}
Or, if your fields are managed by a parent component (via props), you can set the initial value with default: "My default value".
props: {
message: {
type: String,
default: 'My default value',
},
}

Saving an entire form with vuex

I am just starting with Vue and Vuex and am wondering how to go about saving an entire form to an API. I have found this and it only seems like a good solution for a single field. Does this imply I would need to do a custom computed attribute with a getter and setter for each field in the form? I understand how data binding works well for local storage (which seems to be what most examples use) but updating a backend service with every keystroke seems like overkill.
What I would like to do is perform a single commit on a form when the user performs an action (like click a save button) and I feel like making a computed property or method for every field is not the right way to go.
Template:
<div v-show="isEditing" class="edit-view">
<form>
<div class="form-group">
<label>Title</label>
<input :value="item.title" type="text" class="form-control" #input="update" />
</div>
<div class="form-group">
<label>Description</label>
<input :value="item.description" type="text" class="form-control" #input="update" />
</div>
</form>
</div>
JS:
export default {
name: 'todo',
props: ['item'],
data() {
return {
isEditing: false
}
},
methods: {
showEdit() {
this.isEditing = true;
},
update() {
// Commit a change to vuex store
}
}
Keep the form data local to your form component. So define all form properties in data(). Apply v-model to all the input elements and your corresponding data properties. When user clicks submit, make a single commit with the form data.
This way, your form component will contain the edited values, and the vuex store will contain the submitted values.

vuejs - dynamic input 'types'

I would like to be able to dynamically change the type of an input field between text and password. I tried doing the following:
<input :type="dynamicInputType">
data() {
return {
dynamicInputType: 'password'
}
}
But apparently this doesn't work; vuejs displays the error: v-model does not support dynamic input types. Use v-if branches instead.
It's not clear to me how I can fix this with v-if.
This kind of thing is what's being suggested.
<input v-if="'text' === dynamicInputType" type="text">
<input v-else type="password">