About the problem
I am using Laravel 5.6.7, Vue.js. I have modal div which being opened and closed on button click. I type something. Validation fires. I close the modal div. Then clicking button to open it. I see that the validation messages still there.
Component Template
<template>
<div>
<form role="form">
<input name="LastName" type="text" ref="LastName" v-validate
data-vv-rules="required" v-model="createForm.LastName">
<p v-if="errors.has('LastName')">{{ errors.first('LastName') }}</p>
<button v-else type="button" #click="validateBeforeSubmit()">
Create
</button>
</form>
</div>
</template>
Component Script
<script>
export default {
data() {
return {
createForm: {
LastName: ''
}
};
},
created() {
this.InitializeForm();
},
methods: {
InitializeForm() {
this.createForm.LastName = "";
},
validateBeforeSubmit() {
this.$validator.validateAll();
}
}
}
</script>
My findings
if you check the input type text above, I added ref attribute. Tried the below code to set the value to false for aria-invalid attribute.
this.$refs.LastName.setAttribute("aria-invalid", "false");
It sets the attribute value but validation errors are still there. Is there any proper way to get rid of workarounds like above?
I think, when I set the first value or I click on it...some attribute value is being set and due to that form errors occur.
Assuming that you are using vee-validate,
To clear all errors,
this.$validator.errors.clear();
To clear 1 single error only,
this.$validator.errors.remove('LastName');
Add 1 of the code above to the modal close event listener and the error would be gone the next time you opened it..
Related
When I have a click handler in a div that is repeated with v-for, it seems like changes made in that click handler will not be updated in the DOM.
Why?
https://jsfiddle.net/AndersBillLinden/109uzsx7/27/
html:
<div id="vue">
<input type="checkbox" v-model="force_update"/> force update
<div v-for="e in arr">
{{e.id}} = {{e.text}}
click
<span v-show="e.clicked"> clicked</span>
</div>
</div>
js:
new window.Vue(
{
el: '#vue',
data:
{
arr: [{id:1,text:"one"}, {id:2, text:"two"}, {id:3, text:"three"}],
force_update: true
},
methods:
{
on_link_clicked(e)
{
e.clicked = true;
if (this.force_update)
this.$forceUpdate();
}
}
});
clicking link 1
unchecking force update
clicking the 2nd link
(nothing happens)
checking "force update"
Now the changes in the previous step are rendered.
The conclusion is that we sometimes needs to force the update, but it is unclear why.
Change e.clicked = true; to this.$set(e, 'clicked', true) so it adds reactivity to the property which is not already in the model.
I have a simple Vue component that displays an address, but converts into a form to edit the address if the user clicks a button. The address field is an autocomplete using Google Maps API. Because the field is hidden (actually nonexistent) half the time, I have to re-instantiate the autocomplete each time the field is shown.
<template>
<div>
<div v-if="editing">
<div><input ref="autocomplete" v-model="address"></div>
<button #click="save">Save</button>
</div>
<div v-else>
<p>{{ address }}</p>
<button #click="edit">Edit</button>
</div>
</div>
</template>
<script>
export default {
data() {
editing: false,
address: ""
},
methods: {
edit() {
this.editing = true;
this.initAutocomplete();
},
save() {
this.editing = false;
}
initAutocomplete() {
this.autocomplete = new google.maps.places.Autocomplete(this.$refs.autocomplete, {});
}
},
mounted() {
this.initAutocomplete();
}
}
I was getting errors that the autocomplete reference was not a valid HTMLInputElement, and when I did console.log(this.$refs) it only produced {} even though the input field was clearly present on screen. I then realized it was trying to reference a nonexistent field, so I then tried to confine the autocomplete init to only when the input field should be visible via v-if. Even with this, initAutocomplete() is still giving errors trying to reference a nonexistent field.
How can I ensure that the reference exists first?
Maybe a solution would be to use $nextTick which will wait for your DOM to rerender.
So your code would look like :
edit() {
this.editing = true;
this.$nextTick(() => { this.initAutocomplete(); });
},
Moreover if you try to use your this.initAutocomplete(); during mounting it cannot work since the $refs.autocomplete is not existing yet but I'm not sure you need it since your v-model is already empty.
I think it's because your "refs" is plural
<input refs="autocomplete" v-model="address">
It should be:
<input ref="autocomplete" v-model="address">
I have defined a vuejs component this way:
<template>
<form #submit.prevent="submit">
<textarea id="content" cols="30" rows="10" v-model="content">{{ content }}</textarea>
<button class="btn btn-lg btn-success" type="submit" #click="send()">
Send content
</button>
</form>
</template>
<script>
export default {
data() {
return {
content: '// Initial content'
}
},
methods: {
send() {
console.log('Content', this.content);
},
submit() {
return false;
}
},
mounted() {
console.log('Template init ', this.content);
}
}
</script>
When I click on send, the send method outputs the content of the textarea as expected. But when I change the content of the textarea with jquery:
$('#content').val(content);
and hit send, it doesn't update content in the template. "Send" outputs the old value.
can somebody please explain to me what's wrong here?
v-model is listening for an input event to trigger changing the value of its bound variable.
From the vue.js documentation:
v-model internally uses different properties and emits different
events for different input elements:
text and textarea elements use value property and input event;
checkboxes and radiobuttons use checked property and change event;
select fields use value as a prop and change as an event.
Using the JQuery val() method to select the element and change the value does not trigger an event that v-model is listening for, so the value of your bound variable does not change/update.
If you absolutely have to use JQuery to change the content, you could manually trigger an event that might also trigger the v-model binding to update:
$('#content').val(content).trigger('input');
Evening. I've created a button which adds a component that has an input field inside. I might need to press that button few times so there would be 2-3 input fields that appear. Whenever I type the text I would like to send a request from the parent component but I don't know how to retrieve the data from every child component that has been created. Is this the time to start using vuex (never used it)?
ParentComponent.vue
<template>
<div>
<button class="btn btn-success" #click="addStep">Add step</button>
<div v-for="i in count">
<recipe-step v-bind:step-number="i"></recipe-step>
</div>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
addStep() {
this.count += 1;
}
}
}
</script>
StepComponent.vue
<template>
<div>
<div class="from-group">
<label for="step-input"></label>
<input id="step-input" v-model="text" type="text">
</div>
</div>
</template>
<script>
export default {
props: {
stepNumber: {
type: Number,
required: true
}
},
data() {
return {
step: this.stepNumber,
text: ""
}
}
}
</script>
No, you really don't need Vuex yet. As long as you are still dealing with parent-child-component communication, you should be fine. Vuex comes into play when components, spread across the hole component hierarchy, need to exchange information.
Now, you should do something like this:
Don't store the text in the child component. When the input changes, send a Custom Event right to the parent component. Note that
<input v-model="text">
is only syntax sugar for
<input :value="text" #input="text = $event">
Both have the same effect. That's way you can send the input event up to the parent, like this:
<input #input="$emit('input', $event)">
Add another prop to your child component called value which should replace text.
And now you can use v-model in the parent component:
<recipe-step v-model="text">
To store multiple values, just use an array in your data properties.
<recipe-step v-model="textArray[i]">
Vuex can help you on that, however if all you want is to get the input text value back to the parent with the minimum effort you can create a prop called value in the children and then pass it as v-model in the parent.
Since you have a v-for you could make it iterate over a list instead a counter and then pass some prop inside each item as v-model
I am struggling with the following, trying to get a div to show up underneath a text input after someone begins typing in the input:
https://jsfiddle.net/chadcf/3vjn71ap/
The template is:
<div id="app">
<input id="foo"
name="foo"
:value="localValue"
type="text"
placeholder=""
autocomplete="off"
#input="handleInput"
>
<div v-if="show">Testing</div>
</div>
With the following vue code:
new Vue({
el: "#app",
data() {
return {
show: false,
localValue: null
}
},
methods: {
handleInput(e) {
this.show = true;
},
}
});
When you run this, if you type a character in the text input, indeed the div underneath shows up. But in addition the character you just typed vanishes. After that first character though everything works fine.
I think what's going on here is that when the input starts and sets this.show = true, that's happening before the value actually updates. I think... And thus vue re-renders the input but with no value. But I'm not actually sure what to do to handle this properly...
This is happening because localValue isn't being updated by your input. When you start typing show will be set to true, so Vue will update the DOM to show your hidden div. But since localValue is null when the DOM updates your input will be blank since its value is bound to localValue. You can verify this by making handleInput toggle show's value instead of setting it to true and you'll see that every time you type something in the input field the hidden div's visibility will be toggled when the DOM updates but the input will be cleared ..
methods: {
handleInput(e) {
this.show = !this.show;
},
}
So to solve this you'll have to make sure that localValue is being updated by your input. The easiest way is to use v-model ..
<div id="app">
<input id="foo"
name="foo"
v-model="localValue"
type="text"
placeholder=""
autocomplete="off"
#input="handleInput"
>
<div v-if="show">Testing</div>
</div>
JSFiddle
Alternatively you can manually handle the input in your handleInput method and set localValue to the typed value like Austio mentioned in his answer.
Hey so you are pretty close on this thought wise. When you handle input yourself, you have to set the new value when you have new input. In your specific case localValue will always be null which is not what i think you want. I think you are wanting something more like this.
methods: {
handleInput(e) {
this.localValue = e.target.value;
this.show = true;
},
}