I'm using Google autocomplete address form. I found example at google official web page. Everything is fine. Everything works! but it's native Javascript,
I have Vue application and I don't like how I change text input values from JS script. The idea is that when I change something in main input, JS event listener should change values for other inputs:
document.getElementById(addressType).value = val;
Problem is that I should use "document" to change values:
document.getElementById('street_number').value
I would like to have something like tat:
<input type="text" v-model="input.address" ref="addressRef">
And to read values:
export default {
data() {
return {
input: {
address: "",
...
}
};
},
methods: {
test() {
console.log(this.input.address);
console.log(this.$refs.addressRef);
}
}
So the question is:
How to set the value from JS code to update binding values? Right now values are null because I use "getElementById("id").value = val"
You can emit input event afterwards which v-model relies on for updating its value:
let el = document.getElementById("id");
el.value = val;
el.dispatchEvent(new Event('input'));
In action:
Vue.config.devtools = false
const app = new Vue({
el: '#app',
data: {
message: null
},
methods: {
updateBinding() {
let el = document.getElementById("input");
el.value = 'Hello!';
el.dispatchEvent(new Event('input'));
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="updateBinding">Click me </button><br>
<input id="input" v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
</div>
Related
I have a project where users need to paste something into an input field. Inside this event I need to use the pasted data and the clear the input field. But I cannot seem to get it empty.
I have tried clearing the v-model element binded to the input, clearing the input using ref and event.target.blur(). But nothing seems to clear the input when something is pasted inside.
new Vue({
el: "#app",
data: {
todo: ''
},
methods: {
onPaste: function(event) {
let clipped = event.clipboardData.getData('text');
console.log(clipped)
this.todo = ''
this.$refs['todo'].value = '';
event.target.blur();
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-model="todo" ref="todo" #paste="onPaste">
</div>
You can use the prevent modifier on paste to prevent the native event and you don't need to use the $ref in this case.
Check #paste.prevent:
new Vue({
el: "#app",
data: {
todo: ''
},
methods: {
onPaste: function(event) {
let clipped = event.clipboardData.getData('text');
console.log(clipped)
this.todo = '';
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-model="todo" #keyup.enter="addTodo" #paste.prevent="onPaste">
</div>
When an input field requires more info, the browser shows a message in a bubble about why the input is invalid. I would like to prevent the default for this in vue but I'm not sure how. Below is how I would do it in JavaScript but in Vue, there may be a way to do #invalid like how I know you can do #submit on a form as an eventListener. I'm also wondering if extra prevention is needed to prevent this in ios or android.
HTML
<form>
<input type="text" required>
<input type="submit">
</form>
JS
document.querySelector( "input" ).addEventListener( "invalid",
function( event ) {
event.preventDefault();
});
https://codepen.io/albert-anthony4962/pen/BajORVZ
If you want to completely disable validation, you can add novalidate="true" to your form element.
I suspect that you might only want to do that on the initial page load. If so, could you update your section and hopefully and add an example? I can update my answer after that 😀
A pattern I have (idea from Vuetify) is pretty easy:
new Vue({
el: "#app",
data: {
isFormValid: null,
form: {
input_1: {
text: null,
rules: ['required', 'min3'],
validateText: null
},
input_2: {
text: null,
rules: ['required'],
validateText: null
}
},
rules: {
required: v => !!v && !![...v].length || 'This field is required.',
min3: v => !!v && !!([...v].length > 2) || 'This field must be at least 3 characters long.'
}
},
methods: {
validateForm() {
const validated = []
for (let key in this.form) {
const v = this.form[key].rules.map(e => {
return this.rules[e](this.form[key].text)
})
if (v.some(e => e !== true)) {
this.form[key].validateText = v.filter(e => e !== true)[0]
validated.push(false)
} else {
this.form[key].validateText = "This field is valid."
validated.push(true)
}
}
return validated.every(e => e === true)
},
submitForm() {
if (this.validateForm()) {
// submit logic
this.isFormValid = "Yes, it's valid."
} else {
// not valid logic:
this.isFormValid = "No, it's not valid."
}
},
resetValidation() {
const form = JSON.parse(JSON.stringify(this.form))
for (let key in form) {
form[key].validateText = null
}
this.isFormValid = null
this.form = form
},
resetForm() {
const form = JSON.parse(JSON.stringify(this.form))
for (let key in form) {
form[key].validateText = null
form[key].text = null
}
this.isFormValid = null
this.form = form
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<form ref="formRef">
<label for="input_1">
Input 1:
<input
id="input_1"
type="text"
v-model="form.input_1.text"
/>
</label>
<div>This field will validate if NOT EMPTY AND HAS AT LEAST 3 CHARS</div>
<div>{{ form.input_1.validateText || ' ' }}</div>
<br />
<label for="input_2">
Input 2:
<input
id="input_2"
type="text"
v-model="form.input_2.text"
/>
</label>
<div>This field will validate if NOT EMPTY</div>
<div>{{ form.input_2.validateText || ' ' }}</div>
<br />
<button type="submit" #click.prevent="submitForm">
SUBMIT
</button>
<div>Is the form valid? {{ isFormValid }}</div>
</form>
<button #click="resetValidation">RESET VALIDATION</button><br />
<button #click="resetForm">RESET FORM</button>
</div>
This way you don't have to put up with the HTML5 "bubbles", but can still validate your form - in any way you need. You can compose any validation scheme you want by using functions that go over your input text. You could even come up with regexp validation, pattern validation (like phone numbers), etc. It's not the greatest solution, but quite "pluggable".
This is also supposed to be cross-platform (if you use Vue).
I want to calculate the earnings from share using vue. I'm subtracting the day closing amount to the start one. I'm not able to display the result on the Dom.
JSfiddle: https://jsfiddle.net/4bep87sf/
This is the code:
let app = new Vue({
el: '#app',
data: {
s: '',
e: '',
tot: '0'
},
watch: {
e: function(){
this.tot = (this.e + this.s);
return this.f;
}
});
Use a computed property:
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
data: () => ({
s: 0,
e: 0
}),
computed: {
tot() {
return Number(this.s) + Number(this.e);
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input v-model="s" type="number">
<input v-model="e" type="number">
<pre>{{s}} + {{e}} = {{tot}}</pre>
</div>
Also note you need to cast your values as Number() if you want the sum to be correct. If they're interpreted as strings a + b = ab.
Very close to tao answer. Only "fix" two User experience issues (Not directly related to Vue).
Issue 1: "030" or "30" ahhhh:
First, if you set a default value (0 for example), when the user focuses input and type "3" the output is 03! (or 30) ==> Very annoying (Especially on mobile).
Sometimes it's better to set the input value to null and show input placeholder (Fix this annoying issue).
Issue 2 (No meaning result):
The output 0 + 0 = 0 does not contribute too much to the user. Sometimes it's better to put the sum inside v-if.
<p v-if="number1 && number2">{{total}}</p>
Basic code example
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
data: () => ({
number1: {
type: Number,
value: null,
placeholder: "Enter number 1",
},
number2: {
type: Number,
value: null,
placeholder: "Enter number 2",
}
}),
computed: {
total() {
return Number(this.number1.value) + Number(this.number2.value);
}
},
})
span{
color: red;
font-weight: bold
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h3></h3>
<div>
<label>Number 1:</label>
<input autofocus v-model="number1.value" type="number" v-bind:placeholder="number1.placeholder">
</div>
<div>
<label>Number 2:</label>
<input v-model="number2.value" type="number" v-bind:placeholder="number2.placeholder">
</div>
<p>Total:<span v-if="number1.value && number2.value"> {{total}}</span></p>
</div>
v-model.lazy also sometimes useful for calucations:
By default, v-model syncs the input with the data after each input
event (with the exception of IME composition, as stated above). You
can add the lazy modifier to instead sync after change events. https://v2.vuejs.org/v2/guide/forms.html#lazy
I have created a settings page where users can update their email addresses. Everything worked fine but suddenly the validation is not updating anymore. Only the first change of the input field triggers validateState().
Any further changes will not trigger this function so the status of that field stays as it is.
I have compared the code with other components that use the same code and they still work fine.
I am using bootstrap-vue components for the form.
<template>
<div class="row">
<div class="col-md-12">
<b-form #submit="onSubmit">
<b-form-group :label="$t('general.email')"
label-for="settingsEmail"
:invalid-feedback="errors.first('email')">
<b-form-input id="settingsEmail"
type="text"
v-model="form.email"
:disabled="saving"
name="email"
:state="validateState('email')"
v-validate="{required: true, email: true}">
</b-form-input>
</b-form-group>
<b-button type="submit" variant="primary" :disabled="saving || !hasChanged() || errors.any()"><i class="fa fa-refresh fa-spin fa-fw" v-if="saving"></i> {{$t('general.save')}}</b-button>
</b-form>
</div>
</div>
</template>
<script>
import {UPDATE_USER} from '../config/actions'
export default {
name: 'settingsAccount',
data() {
return {
form: {},
saving: false
}
},
computed: {
user: function() {
return this.$store.getters.getUser;
}
},
created() {
this.init();
},
methods: {
init() {
this.form.email = this.user.email;
},
hasChanged() {
if(this.form.email !== this.user.email) {
return true;
}
return false;
},
onSubmit(event) {
event.preventDefault();
this.saving = true;
this.$validator.validateAll().then((result) => {
if (result) {
let data = {};
if(this.form.email !== this.user.email) {
data.email = this.form.email;
}
this.$store.dispatch(UPDATE_USER, data).then(() => {
this.saving = false;
this.$validator.reset();
}).catch(() => {
this.saving = false;
});
} else {
this.saving = false;
}
});
},
validateState(ref) {
if (this.veeFields[ref] && (this.veeFields[ref].dirty || this.veeFields[ref].validated)) {
return !this.errors.has(ref)
}
return null
},
}
}
</script>
The problem you're having is that the form data element is an empty object, so it will only trigger reactivity when the whole object changes. Either you need to change your data to be this:
data() {
return {
form: {email:''},
saving: false
}
},
Or in your init function, explicitly add the email property as reactive:
methods: {
init() {
this.$set(form,'email',this.user.email)
},
//...
If you're not clear on why, you can read the details here: https://v2.vuejs.org/v2/guide/reactivity.html
A working example (minus vuex) here: https://codesandbox.io/s/x4kp93w3o
PS, when writing questions about vue, it's very helpful to boil it down to a simpler example. Get rid of vuex, remove your translation stuff. Sometimes the answer will jump out at you once you have it as simple as possible.
I found a jsfiddle example that I forked and then edited. I don't understand what's going on or how to fix it. In my example I'm using checkboxes with values but when I click a checkbox the value is changed to true or false depending on if the checkbox is clicked.
const Checkboxes = {
template: '#checkboxTmpl',
data() {
return {
text: '',
options: [
{
name: 'Web',
slug: 'web'
},
{
name: 'iOS',
slug: 'ios'
},
{
name: 'Android',
slug: 'android'
}
]
};
},
created() {
this.$validator.extend('oneChecked', {
getMessage: field => 'At least one ' + field + ' needs to be checked.',
validate: (value, [testProp]) => {
const options = this.options;
// console.log('questions', value, testProp, options.some((option) => option[testProp]));
return value || options.some((option) => option[testProp]);
}
});
},
methods: {
validateBeforeSubmit(e) {
this.$validator.validateAll(); // why is oneChecked not validated here? --> manually trigger validate below
this.options.forEach((option) => {
this.$validator.validate('platforms', option.slug, ['checked'])
});
console.log('validator', this.errors);
if (!this.errors.any()) {
alert('succesfully submitted!');
}
}
}
};
Vue.use(VeeValidate);
const app = new Vue({
el: '#app',
render: (h) => h(Checkboxes)
})
<script src="https://cdn.jsdelivr.net/vee-validate/2.0.0-beta.18/vee-validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.js"></script>
<div id="app">
</div>
<script id="checkboxTmpl" type="text/template">
<form #submit.prevent="validateBeforeSubmit">
<label v-for="(option, index) in options">
<input type="checkbox"
v-model="option.slug"
name="platform"
v-validate.initial="option.slug"
data-vv-rules="oneChecked:checked"
data-vv-as="platform"/> {{option.name}}
</label>
<p v-show="errors.has('platform')">{{ errors.first('platform') }}</p>
<pre>{{options}}</pre>
<button type="submit">Submit</button>
</form>
</script>
I don't understand why all of the checkboxes are checked and unchecking one of them returns a validation error even though two are still checked. I like that errors are shown before the form is submitted but unchecking all and then submitting doesn't trigger the validation error.
I'm using VeeValidate because that is what the example uses but any other solution would be fine. I don't want to use jQuery in my vue.js application.
I would really like to understand what is going on.
There was two main problems going on :
Using v-model on the wrong key. In fact, each time the checkbox was checked or unchecked, it will emit an input event that will modify the original slug of the option (in your data). Instead, you need to add a checked field in your option. Then in your template add the :checked attribute and modify your v-model to be :option.checked.
As the docs of VeeValidate say, you can just use the required rule to make sure a checkbox has to be checked to submit your form. Here is the link towards the docs. Therefore, you don't need your created block.
Additionally, the validateAll function returns a promise containing the result of the validation. So no need to use this.errors.any() too.
Also, I upgraded the VeeValidate library to the latest as you used a beta.
Here is the working code :
const Checkboxes = {
template: '#checkboxTmpl',
data() {
return {
text: '',
options: [{
name: 'Web',
slug: 'web',
checked: false
},
{
name: 'iOS',
slug: 'ios',
checked: true
},
{
name: 'Android',
slug: 'android',
checked: true
}
]
};
},
methods: {
validateBeforeSubmit(e) {
this.$validator.validateAll().then(value => {
if (value) {
alert('successfully submitted')
}
})
}
}
};
Vue.use(VeeValidate);
const app = new Vue({
el: '#app',
render: (h) => h(Checkboxes)
})
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.js"></script>
<script src="https://unpkg.com/vee-validate#latest"></script>
<script id="checkboxTmpl" type="text/template">
<form #submit.prevent="validateBeforeSubmit">
<label v-for="(option, index) in options">
<input type="checkbox"
:checked="option.checked"
v-model="option.checked"
name="platform"
v-validate="'required'"/> {{option.name}}
</label>
<p v-show="errors.has('platform')">{{ errors.first('platform') }}</p>
<pre>{{options}}</pre>
<button type="submit">Submit</button>
</form>
</script>
Hope that helps!