Clearing input in vuejs form - javascript

Just completed a todolist tutorial.
When submitting the form the input field doesn't clear.
After trying both:
document.getElementById("todo-field").reset();
document.getElementById("#todo-field").value = "";
The input field properly clears but it also deletes the todo.
It seems to delete the input field before it has time to push the new todo in the todos.text array.
Would love some input guys! Thanks!!
<template>
<form id="todo-field" v-on:submit="submitForm">
<input type="text" v-model="text">
</form>
<ul>
<li v-for="todo in todos">
<input class="toggle" type="checkbox" v-model="todo.completed">
<span :class="{completed: todo.completed}" class="col-md-6">
<label #dblclick="deleteTodo(todo)">
{{todo.text}}
</label>
</span>
</li>
</ul>
<script>
export default {
name: 'todos',
data () {
return {
text: '',
todos: [
{
text:'My Todo One',
completed: false
},
{
text:'My Todo Two',
completed: false
},
{
text:'My Todo Three',
completed: false
}
]// End of array
}
},
methods: {
deleteTodo(todo){
this.todos.splice(this.todos.indexOf(todo),1);
},
submitForm(e){
this.todos.push(
{
text: this.text,
completed: false
}
);
//document.getElementById("todo-field").reset();
document.getElementById("#todo-field").value = "";
// To prevent the form from submitting
e.preventDefault();
}
}
}
</script>

These solutions are good but if you want to go for less work then you can use $refs
<form ref="anyName" #submit="submitForm">
</form>
<script>
methods: {
submitForm(){
// Your form submission
this.$refs.anyName.reset(); // This will clear that form
}
}
</script>

What you need is to set this.text to an empty string in your submitForm function:
submitForm(e){
this.todos.push(
{
text: this.text,
completed: false
}
);
this.text = "";
// To prevent the form from submitting
e.preventDefault();
}
Remember that binding works both ways: The (input) view can update the (string) model, or the model can update the view.

Assuming that you have a form that is huge or simply you do not want to reset each form field one by one, you can reset all the fields of the form by iterating through the fields one by one
var self = this;
Object.keys(this.data.form).forEach(function(key,index) {
self.data.form[key] = '';
});
The above will reset all fields of the given this.data.form object to empty string. Let's say there are one or two fields that you selectively want to set to a specific value in that case inside the above block you can easily put a condition based on field name
if(key === "country")
self.data.form[key] = 'Canada';
else
self.data.form[key] = '';
Or if you want to reset the field based on type and you have boolean and other field types in that case
if(typeof self.data.form[key] === "string")
self.data.form[key] = '';
else if (typeof self.data.form[key] === "boolean")
self.data.form[key] = false;
For more type info see here
A basic vuejs template and script sample would look as follow
<template>
<div>
<form #submit.prevent="onSubmit">
<input type="text" class="input" placeholder="User first name" v-model="data.form.firstName">
<input type="text" class="input" placeholder="User last name" v-model="data.form.lastName">
<input type="text" class="input" placeholder="User phone" v-model="data.form.phone">
<input type="submit" class="button is-info" value="Add">
<input type="button" class="button is-warning" #click="resetForm()" value="Reset Form">
</form>
</div>
</template>
See ow the #submit.prevent="onSubmit" is used in the form element. That would by default, prevent the form submission and call the onSubmit function.
Let's assume we have the following for the above
<script>
export default {
data() {
return {
data: {
form: {
firstName: '',
lastName: '',
phone: ''
}
}
}
},
methods: {
onSubmit: function() {
console.log('Make API request.')
this.resetForm(); //clear form automatically after successful request
},
resetForm() {
console.log('Reseting the form')
var self = this; //you need this because *this* will refer to Object.keys below`
//Iterate through each object field, key is name of the object field`
Object.keys(this.data.form).forEach(function(key,index) {
self.data.form[key] = '';
});
}
}
}
</script>
You can call the resetForm from anywhere and it will reset your form fields.

For reset all field in one form you can use event.target.reset()
const app = new Vue({
el: '#app',
data(){
return{
name : null,
lastname : null,
address : null
}
},
methods: {
submitForm : function(event){
event.preventDefault(),
//process...
event.target.reset()
}
}
});
form input[type=text]{border-radius:5px; padding:6px; border:1px solid #ddd}
form input[type=submit]{border-radius:5px; padding:8px; background:#060; color:#fff; cursor:pointer; border:none}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.6/vue.js"></script>
<div id="app">
<form id="todo-field" v-on:submit="submitForm">
<input type="text" v-model="name"><br><br>
<input type="text" v-model="lastname"><br><br>
<input type="text" v-model="address"><br><br>
<input type="submit" value="Send"><br>
</form>
</div>

Markup
<template lang="pug">
form
input.input(type='text' v-model='formData.firstName')
input.input(type='text' v-model='formData.lastName')
button(#click='resetForm') Reset Form
</template>
Script
<script>
const initFromData = { firstName: '', lastName: '' };
export default {
data() {
return {
formData: Object.assign({}, initFromData),
};
},
methods: {
resetForm() {
// if shallow copy
this.formData = Object.assign({}, initFromData);
// if deep copy
// this.formData = JSON.parse(JSON.stringify(this.initFromData));
},
},
};
</script>
Read the difference between a deep copy and a shallow copy HERE.

I use this
this.$refs['refFormName'].resetFields();
this work fine for me.

This solution is only for components
If we toggle(show/hide) components using booleans then data is also removed.
No need to clean the form fields.
I usually make components and initialize them using booleans.
e.g.
<template>
<button #click="show_create_form = true">Add New Record</button
<create-form v-if="show_create_form" />
</template>
<script>
...
data(){
return{
show_create_form:false //making it false by default
}
},
methods:{
submitForm(){
//...
this.axios.post('/submit-form-url',data,config)
.then((response) => {
this.show_create_form= false; //hide it again after success.
//if you now click on add new record button then it will show you empty form
}).catch((error) => {
//
})
}
}
...
</script>
When use clicks on edit button then this boolean becomes true and after successful submit I change it to false again.

I had a situation where i was working with a custom component and i needed to clear the form data.
But only if the page was in 'create' form state, and if the page was not being used to edit an existing item. So I made a method.
I called this method inside a watcher on custom component file, and not the vue page that uses the custom component. If that makes sense.
The entire form $ref was only available to me on the Base Custom Component.
<!-- Custom component HTML -->
<template>
<v-form ref="form" v-model="valid" #submit.prevent>
<slot v-bind="{ formItem, formState, valid }"></slot>
</v-form>
</template>
watch: {
value() {
// Some other code here
this.clearFormDataIfNotEdit(this)
// Some other code here too
}
}
... some other stuff ....
methods: {
clearFormDataIfNotEdit(objct) {
if (objct.formstate === 'create' && objct.formItem.id === undefined) {
objct.$refs.form.reset()
}
},
}
Basically i checked to see if the form data had an ID, if it did not, and the state was on create, then call the obj.$ref.form.reset() if i did this directly in the watcher, then it would be this.$ref.form.reset() obvs.
But you can only call the $ref from the page which it's referenced.
Which is what i wanted to call out with this answer.

This is how I do it in Vue 3.
html:
<input type="text" v-model="state.name">
js:
import {reactive} from "vue";
const state = reactive({
name: ""
})
axios.post('/contact', state)
.then(res => {
if (res.status == 200) {
state.name = ""
}
})
Response status 200 being a successful submission of the form input. state.name is reactive and will be set to "" if the submission is successful.

if your using vue.js v-form you can simply do like
this.form.reset()
Documentation
Vform - Documentation

Related

Vue.js 3: how to pre-populate bound form with props

I am trying to create a simple CRUD app with Vuejs 3.
I have a homepage with a form (as a child component) and a table with created items (as another child component). I submit data via the form to API/database and the table updates. So far so good.
Then, for the update phase, I would like to have a detail page for each item where I also would have the form (the same component reused). But the idea is that form fields would be pre-populated with data from API/Database.
The table on the homepage has a route-link to a detail page and I am passing the id of the item as params. The detail page makes request to API based on id, receives item data and passes them as props into the form component.
If I try to render data directly into template like this, it works fine:
<p v-if="submitType === 'update' && item.id">{{ item.id }}</p>
Now, form fields are bound by v-model to data (form.id for example). But when I try to repopulate it as below, I always get undefined values.
data() {
return {
form: {
id: this.submitType === 'update' ? this.item.id : 0,
}
}
},
I suspect that problem is that the parent call to API is asynchronous and the passing of props is delayed. Because when I pass as props some hardcoded value, it appears as a value in the form field with no problem. Also if the form is shown only when props are received (with the v-if directive), the data.form.id is still undefined.
So is there any way how to pre-populate bound form fields with received props and still have the form component reused for insert and update actions? The rest of the relevant code is below. Thank you very much in advance
// Detail Page
<template>
<Form :item="item" submit-type="update"></Form>
</template>
<script>
export default {
data() {
return {
item: {}
}
},
created() {
callAPI(id).then( response => this.item = response.data )
}
}
</script>
// Form Component
<template>
<p v-if="submitType === 'update' && item.id">{{ item.id }}</p>
<div v-if="submitType === 'insert' || (submitType === 'update' && item.id )">
<section>
<form #submit.prevent="onSubmit">
<div>
<label for="id">ID</label>
<input id="id" name="id" v-model="form.id" type="number" placeholder="ID">
</div>
<input type="submit" value="Save">
</form>
</section>
</div>
</template>
<script>
export default {
name: 'Form',
props: {
item: {
type: Object
},
submitType: {
type: String
}
},
data() {
return {
form: {
id: this.submitType === 'update' ? this.item.id : 0,
}
}
},
}
</script>
You can try with watchers, take a look at following snippet:
const app = Vue.createApp({
data() {
return {
item: {},
type: 'update'
}
},
methods: {
change() {
this.type === 'update' ? this.type = 'insert' : this.type = 'update'
}
},
created() {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(json => this.item = json)
//callAPI(id).then( response => this.item = response.data )
}
})
app.component('myForm', {
template: `
<p v-if="submitType === 'update' && item.id">{{ item.id }}</p>
<div v-if="submitType === 'insert' || (submitType === 'update' && item.id )">
<section>
<form #submit.prevent="onSubmit">
<div>
<label for="id">ID</label>
<input id="id" name="id" v-model="form.id" type="number" placeholder="ID">
</div>
<input type="submit" value="Save">
</form>
</section>
</div>
`,
props: {
item: {
type: Object
},
submitType: {
type: String
}
},
data() {
return {
form: {}
}
},
methods: {
fillData() {
this.submitType === 'update' ? this.form = {...this.item} : this.form = {id: 0}
}
},
watch: {
item() {
this.fillData()
},
submitType() {
this.fillData()
}
},
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<button #click="change">switch type</button>
{{type}}
<my-form :item="item" :submit-type="type"></my-form>
</div>

Vue: Best way to submit form of child component from the parent component through method

I have 2 components.
Parent Component:
Vue.component('parent', {
template: '<div><child :counter="counter" :isSubmit="isSubmit" showButton="true"></child>'+
'<button v-on:click="changeCounter">Change Counter</button></div>',
data () {
return {
counter: 2,
isSubmit : false
}
},
methods : {
changeCounter () {
//retrieve value of counter dynamically let's say through ajax call.
this.counter = 9;
this.isSubmit = true;
}
}
})
Child Component:
Vue.component('child', {
template: '<form action="test.php" method="GET" ref="childForm">'+
'<input type="hidden" v-model="counter" name="counter" />'+
'<input type="submit" v-if="showButton" /></form>',
props: {
counter : {
type: Number,
default: 0
},
showButton:false,
isSubmit: false
},
watch : {
isSubmit (val) {
console.log("Inside watcher");
this.submitForm();
}
},
methods : {
submitForm () {
console.log(this.counter);// output-9
this.$refs.childForm.submit();
},
}
})
index.html
....
<div id="app>">
<parent></parent>
<parent></parent>
</div>
....
In this example, When I click on the "Change Counter" button, the form is submitted with the older value of the counter (i.e. submitted to /test.php?counter=2). Although props of child component are reactive in dev tools (counter = 9) it does not reflect while submitting the form. But it really works if I submit the form by the submit button on the child component (i.e. submitted to /test.php?counter=9).
Your help is appreciated. Please help me to understand why this kind of behaviour is shown and also seeking the solutions.
Thanks in advance.
Quick note
Since you're using GET requests, you could skip the whole <form> thing and just go directly to the URL
methods: {
changeCounter () {
this.counter = 9
window.location = `test.php?counter=${this.counter}`
}
}
Longer answer
You need to wait for the counter change to update the DOM in order to use a normal form submission.
To wait for state changes to effect the DOM, use $nextTick. I would also advise submitting the form via the submitForm method rather than watching a Boolean value. You can access the method by using refs.
Vue.component('child', {
template: `<form action="test.php" method="GET" ref="childForm">
<input type="hidden" :value="counter" name="counter" />
<input type="submit" v-if="showButton" />
</form>`,
props: {
counter : {
type: Number,
default: 0
},
showButton: Boolean
},
methods: {
async submitForm () {
await this.$nextTick() // wait for the DOM to update
this.$refs.childForm.submit()
}
}
})
Vue.component("parent", {
template: `<div>
<child :counter="counter" :show-button="true" ref="form"></child>
<button #click="changeCounter">Change Counter</button>
</div>`,
data: () => ({ counter: 2 }),
methods: {
changeCounter () {
this.counter = 9
this.$refs.form.submitForm() // call the child component method
}
}
})

Why is the Vue.js input value not updating?

I have a Vue.js text-input component like the following:
<template>
<input
type="text"
:id="name"
:name="name"
v-model="inputValue"
>
</template>
<script>
export default {
props: ['name', 'value'],
data: function () {
return {
inputValue: this.value
};
},
watch: {
inputValue: function () {
eventBus.$emit('inputChanged', {
type: 'text',
name: this.name,
value: this.inputValue
});
}
}
};
</script>
And I am using that text-input in another component as follows:
<ul>
<li v-for="row in rows" :key="row.id">
<text-input :name="row.name" :value="row.value">
</text-input>
</li>
</ul>
Then, within the JS of the component using text-input, I have code like the following for removing li rows:
this.rows = this.rows.filter((row, i) => i !== idx);
The filter method is properly removing the row that has an index of idx from the rows array, and in the parent component, I can confirm that the row is indeed gone, however, if I have, for example, two rows, the first with a value of 1 and the second with a value of 2, and then I delete the first row, even though the remaining row has a value of 2, I am still seeing 1 in the text input.
Why? I don't understand why Vue.js is not updating the value of the text input, even though the value of value is clearly changing from 1 to 2, and I can confirm that in the parent component.
Maybe I'm just not understanding how Vue.js and v-model work, but it seems like the value of the text input should update. Any advice/explanation would be greatly appreciated. Thank you.
You cannot mutate values between components like that.
Here is a sample snippet on how to properly pass values back and forth. You will need to use computed setter/getter. Added a button to change the value and reflect it back to the instance. It works for both directions.
<template>
<div>
<input type="text" :id="name" v-model="inputValue" />
<button #click="inputValue='value2'">click</button>
</div>
</template>
<script>
export default {
props: ['name', 'value'],
computed: {
inputValue: {
get() {
return this.value;
},
set(val) {
this.$emit('updated', val);
}
}
}
}
</script>
Notice that the "#updated" event updates back the local variable with the updated value:
<text-input :name="row.name" :value="row.value" #updated="item=>row.value=item"></text-input>
From your code you are trying to listen to changes.. in v-model data..
// Your Vue components
<template>
<input
type="text"
:id="name"
:name="name"
v-model="inputValue"
>
</template>
<script>
export default {
props: ['name', 'value'],
data: function () {
return {
inputValue: ""
};
},
};
</script>
If You really want to listen for changes..
<ul>
<li v-for="row in rows" :key="row.id">
<text-input #keyup="_keyUp" :name="row.name" :value="row.value">
</text-input>
</li>
</ul>
in your component file
<template>...</template>
<script>
export default {
props: ['name', 'value'],
data: function () {
return {
inputValue: ""
};
},
methods : {
_keyUp : () => {// handle events here}
};
</script>
check here for events on input here
To bind value from props..
get the props value, then assign it to 'inputValue' variable
it will reflect in tthe input element

Vue.Js, binding a value to a checkbox in a component

I'm making a component which is a wrapper around a checkbox (I've done similar with inputs of type 'text' and 'number') but I cannot get my passed in value to bind correctly.
My component is:
<template>
<div class="field">
<label :for="name" class="label">
{{ label }}
</label>
<div class="control">
<input :id="name" :name="name" type="checkbox" class="control" :checked="value" v-on="listeners" />
</div>
<p v-show="this.hasErrors" class="help has-text-danger">
<ul>
<li v-for="error in errors" :key="error">{{ error }}</li>
</ul>
</p>
</div>
</template>
<script>
export default {
name: 'check-edit',
props: {
value: {
type: Boolean,
default: false
},
label: {
type: String,
default: ''
},
name: {
type: String,
default: ''
},
errors: {
type: Array,
default: () => []
}
},
mounted () {
},
computed: {
listeners () {
return {
// Pass all component listeners directly to input
...this.$listeners,
// Override input listener to work with v-model
input: event => this.$emit('input', event.target.value)
}
},
hasErrors () {
return this.errors.length > 0
}
},
}
</script>
I've imported it globally; and am invoking it in another view by doing:
<check-edit name="ShowInCalendar" v-model="model.ShowInCalendar" label="Show in calendar?" :errors="this.errors.ShowInCalendar"></check-edit>
My model is in data and the property ShowInCalendar is a boolean and in my test case is true. So when I view the page the box is checked. Using the Vue tools in firefox I can see the model.ShowInCalendar is true, and the box is checked. However, when I click it the box remains checked and the value of ShowInCalendar changes to 'on', then changes thereafter do not change the value of ShowInCalendar.
I found this example here: https://jsfiddle.net/robertkern/oovb8ym7/ and have tried to implement a local data property for it but the result is not working.
The crux of what I'm trying to do is have the initial checkstate of the checkbox be that of ShowInCalendar (or whatever property is bound via v-model on the component) and then have that property be update (to be true or false) when the checkbox is checked.
Can anyone offer me any advice please?
Thank you.
You should not $emit event.target.value, it's the value of the checkbox, it's not a Boolean value. If you want to detect the checkbox is update(to be true or false) or not, You should $emit event.target.checked just like fstep said.
If v-on is the only listener that will be used it might be easier to use v-model as in the checkbox example from the Vue input docs.
However you can use listeners based on Binding-Native-Events-to-Components docs
<template>
<div class="field">
<label :for="name" class="label">
{{ label }}
</label>
<div class="control">
<input :id="name" :name="name" type="checkbox" class="control" checked="value" v-on="listeners" />
</div>
<p v-show="this.hasErrors" class="help has-text-danger">
<ul>
<li v-for="error in errors" :key="error">{{ error }}</li>
</ul>
</p>
</div>
</template>
<script>
export default {
name: 'check-edit',
props: {
value: {
type: Boolean,
default: false
},
label: {
type: String,
default: ''
},
name: {
type: String,
default: ''
},
errors: {
type: Array,
default: () => []
}
},
mounted() {},
computed: {
listeners() {
var vm = this;
// `Object.assign` merges objects together to form a new object
return Object.assign(
{},
// We add all the listeners from the parent
this.$listeners,
// Then we can add custom listeners or override the
// behavior of some listeners.
{
// This ensures that the component works with v-model
input: function(event) {
vm.$emit('input', event.target.checked);
}
}
);
},
hasErrors() {
return this.errors.length > 0;
}
}
};
</script>
Don't change props. Your component, having a v-model, should be emitting input events on change. The parent will handle the actual changing of the value.

How to submit a form using vue.js 2?

My view blade laravel like this :
<form slot="search" class="navbar-search" action="{{url('search')}}">
<search-header-view></search-header-view>
</form>
The view blade laravel call vue component (search-header-view component)
My vue component(search-header-view component) like this :
<template>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" placeholder="Search" name="q" autofocus v-model="keyword">
<span class="input-group-btn">
<button class="btn btn-default" type="submit" id="submit-search"><span class="fa fa-search"></span></button>
</span>
<ul v-if="!selected && keyword">
<li v-for="state in filteredStates" #click="select(state.name)">{{ state.name }}</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: 'SearchHeaderView',
data() {
return {
baseUrl: window.Laravel.baseUrl,
keyword: null,
selected: null,
filteredStates: []
}
},
watch: {
keyword(value) {
this.$store.dispatch('getProducts', { q: value })
.then(res => {
this.filteredStates = res.data;
})
}
},
methods: {
select: function(state) {
this.keyword = state
this.selected = state
document.getElementById('submit-search').submit()
},
input: function() {
this.selected = null
}
},
}
</script>
I want to submit the form when user click the keyword
I try document.getElementById('submit-search').submit()
But on the console exist error like this :
TypeError: document.getElementById(...).submit is not a function
How can I solve this error?
You need to call submit() on the <form> element (which is the root element of the component):
this.$el.submit();
EDIT: Since you have updated your question, the above no longer applies.
To trigger a button click, just call click() on the button element:
<button ref="submitButton">Foo</button>
this.$refs.submitButton.click();
This is fine if your components are meant to be tightly-coupled, otherwise a cleaner solution would be to $emit an event from your <search-header-view> component and respond to it in the parent component, like this:
this.$emit('submit');
In parent component:
<search-header-view #submit="submit">
methods: {
submit() {
// Submit the form manually here (add a ref to your form element)
this.$refs.form.submit();
}
}
In the case where it says that submit is not a function even though you reference it correclt with $refs:
It looks like if a form control has a name or id of submit it will mask the form's submit method.
In my case I had name="submit" on my submit button, I removed it and it worked fine.
Just provide the #submit listener to your form :
<form slot="search" class="navbar-search" #submit="submited">
<search-header-view></search-header-view>
</form>
And the associated method :
methods: {
submited (e) {
// e is the form event, e.target is the form, do your things on submit
}
}

Categories