Saving an entire form with vuex - vuejs2

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.

Related

VueJs handle Form Submit on Enter press

I have a background in Angular. Starting with vue was an okay experience for me until I came across a problem which VueJS developers seem to have shit on and slid under the carpet.
How can we create a form in which user can press enter from an input field to submit the form
This was seriously disappointing.
and please if you know the answer be kind enough to post in the Official vue documentation as well.
*Note:
my workaround: I used v-on:keydown.enter.prevent='loginUser' on every input field.
is there any way to not use it this way ( on every input field).
With button type as submit as well, form gets submitted when Enter key is pressed on any input.
No explicit binding is required on keypress.
new Vue({
el: '#app',
data() {
return {
title: "Vue 2 -Form submission on Enter",
formInput:{
fname:'',
lname:'',
gender:'male'
}
};
},
methods:{
onSubmit(){
console.log('submitted', this.formInput)
}
}
})
.form{
display:flex;
flex-direction:column;
gap:10px;
max-width:200px
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"><h2>{{title}}</h2>
<form v-on:submit.prevent="onSubmit" class="form">
<input v-model="formInput.fname" #keydown.enter.prevent placeholder="first name"/>
<input v-model="formInput.lname" placeholder="last name"/>
<div>
<input v-model="formInput.gender" name="gender" placeholder="Gender" type="radio" value="male"/>male
<input v-model="formInput.gender" name="gender" placeholder="Gender" type="radio" value="female"/>Female
</div>
<button type="submit">Submit</button>
<pre>{{formInput}}</pre>
</form>
</div>
I'd urge you to check out this page on the VueJS documentation.
It explains how to set events on certain interactions. For example, you can trigger a function call on pressing the Enter key within an input field by doing this:
<input type="text" #keyup.enter="submit">
This will call the submit() method when the Enter key is pressed and released (keyup). On press, you can use keydown instead.'
In fact, the example I've taken is directly from this section in the page I linked above.
EDIT: A pure-HTML way to do this is to set your input type as submit, which will allow Enter to submit the form
You need to wrap your inputs within <form> ... </form> tag

b-field value is getting updated only #select and not #input?

I am using the Buefy UI components in my VueJS project. I have a drop-down in a page:
<b-field label="Business Unit">
<b-autocomplete
:data="dataBusinessUnit"
placeholder="select a business unit..."
field="businessUnit"
:loading="isFetching"
:value="this.objectData.businessUnit"
#typing="getAsyncDataBusinessUnit"
#select="(option) => {updateValue(option.id,'businessUnit')}"
>
<template slot-scope="props">
<div class="container">
<p>
<b>ID:</b>
{{props.option.id}}
</p>
<p>
<b>Description:</b>
{{props.option.description}}
</p>
</div>
</template>
<template slot="empty">No results found</template>
</b-autocomplete>
</b-field>
As you can see from the above code, the updateValue function is responsible for updating the value, but it will currently be called only when the user selects something from the drop-down suggestions. I want the value to be updated even when the user starts to type something. Example: #input="(newValue)=>{updateValue(newValue,'businessUnit')}". However, there is already a debounce function called getAsyncDataBusinessUnit that I am calling to fetch the filtered autocomplete results based on what the user has typed during the #typing event.
According to the Buefy Autocomplete API documentation found here, you could probably use v-model instead of using value directly.
Alternatively you could actually implement the #input like you wrote yourself, the #typing event shouldn't interfere with it.
Or you could just handle the value updating in #typing:
#typing="onTyping"
// then later in JS...
methods: {
onTyping(value) {
this.updateValue(value, 'businessUnit')
this.getAsyncDataBusinessUnit(value)
},
}

vue computed placeholder changes multiple select-boxes

Vue version 2.6.10
I'll try writing in the code that gives the relevant information to prevent this from being huge
this is part of my component that has to do with select boxes
<div class="input-field">
<input
:id="name"
v-model="searchFilter"
type="text"
tabindex="-1"
:class="{ searchbar: true, 'validation-error': validateError }"
autocomplete="off"
spellcheck="false"
:disabled="disabled || loading"
:readonly="single"
:placeholder="placeholder"
#click="openList"
/>
<input-icon :loading="loading"></input-icon>
</div>
this is the computed part which does the placeholder part
computed: {
placeholder() {
if (this.single) {
const selected = this.singleList.filter(item => item.selected === true).shift();
return selected === undefined ? `Select ${_.startCase(this.name)}` : selected.name;
}
},
},
The issue is, that lets say I've got 3 instances of this component running?
once I choose one of them? the rest change their UI (aka placeholder value)
This is strictly a ui problem since I can tell that the value stays the same but I can't seem to find a way to access that value in order to show it.
I hope this is enough information to go on
Will provide additional code if needed.
Thanks in advance.

How to avoid vue component redraw?

I have prepared tag input control in Vue with tag grouping. Templates includes:
<script type="text/x-template" id="tem_vtags">
<div class="v-tags">
<ul>
<li v-for="(item, index) in model.items" :key="index" :data-group="getGroupName(item)"><div :data-value="item"><span v-if="typeof model.tagRenderer != 'function'">{{ item }}</span><span v-if="typeof model.tagRenderer == 'function'" v-html="tagRender(item)"></span></div><div data-remove="" #click="remove(index)">x</div></li>
</ul>
<textarea v-model="input" placeholder="type value and hit enter" #keydown="inputKeydown($event,input)"></textarea>
<button v-on:click="add(input)">Apply</button>
</div>
</script>
I have defined component method called .getGroupName() which relays on other function called .qualifier() that can be set over props.
My problem: once I add any tags to collection (.items) when i type anything into textarea for each keydown .getGroupName() seems to be called. It looks like entering anything to textarea results all component rerender?
Do you know how to avoid this behavior? I expect .getGroupName to be called only when new tag is added.
Heres the full code:
https://codepen.io/anon/pen/bKOJjo?editors=1011 (i have placed debugger; to catch when runtime enters .qualifier().
Any help appriciated.
It Man
TL;DR;
You can't, what you can do is optimize to reduce function calls.
the redraws are dynamic, triggered by data change. because you have functions (v-model and #keydown) you will update the data. The issue is that when you call a function: :data-group="getGroupName(item)" it will execute every time, because it makes no assumptions on what data may have changed.
One way of dealing with is is setting groupName as a computed key-val object that you can look up without calling the function. Then you can use :data-group="getGroupName[item]" without calling the function on redraw. The same should be done for v-html="tagRender(item)"
Instead of trying to fight how the framework handles data input events and rendering, instead use it to your advantage:
new Vue({
el: '#app',
template: '#example',
data() {
return {
someInput: '',
someInputStore: []
};
},
methods: {
add() {
if (this.someInputStore.indexOf(this.someInput) === -1) {
this.someInputStore.push(this.someInput);
this.someInput = '';
}
},
}
});
<html>
<body>
<div id="app"></div>
<template id="example">
<div>
<textarea v-model="someInput" #keyup.enter.exact="add" #keyup.shift.enter=""></textarea>
<button #click="add">click to add new input</button>
<div>
{{ someInputStore }}
</div>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
</body>
</html>
Event modifiers modifying
In the example, you can see that I am using 4 different event modifiers in order to achieve the desired outcome, but I will focus on the combination of them here:
#keyup.enter.exact - allows control of the exact combination of system modifiers needed to trigger an event. In this case, we are looking for the enter button.
#keyup.shift.enter - this is the interesting bit. Instead of trying to hackily prevent the framework from firing on both events, we can use this (and a blank value) to prevent the event we added into #keyup.enter.exact from firing. I must note that ="" is critical to the whole setup working properly. Without it, you aren't giving vue an alternative to firing the add method, as shown by my example.
I hope this helps!

Handling form submission without replacing template in VueJS

First of all, please be kind. I'm new to VueJS coming from the Angular world where things are different ;-)
I am creating a multi-page website using VueJS for simple things like a floaty header and submission of forms etc. I'd like the markup for my contact form to be in my HTML (rendered by the CMS) and I'd like to have VueJS handle the form submission and replacing the form with a thank-you message. So, a simplified version of the form would look like this.
<contact-form>
<form class="contact-form_form">
...
<input name="emailaddress" type="text" />
...
<button></button>
</form>
<div class="contact-form_thanks">
Thanks for filling in this lovely form
</div>
</contact-form>
So, the obvious thing to do is to create a VueJS component, but I don't want it to introduce a new template, I just want it to submit the form when the button is pressed (using Axios) and hide the form and show the thank you message.
I know how to do all of this in angular using attribute directives and ng-show/hide etc. but can't really see how to do this in VueJS because all the tutorials are geared to wards SPAs and Single file components with templates.
Any kick in the right direction would be appreciated.
Seems like you just want a data item indicating whether the form has been submitted, and v-if and v-else to control what displays in either case.
new Vue({
el: 'contact-form',
components: {
contactForm: {
data() {
return { hasBeenSubmitted: false };
},
methods: {
doSubmit() {
console.log("Some behind-the-scenes submission action...");
this.hasBeenSubmitted = true;
}
}
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<contact-form inline-template>
<div v-if="hasBeenSubmitted" class="contact-form_thanks">
Thanks for filling in this lovely form
</div>
<form v-else class="contact-form_form">
...
<input name="emailaddress" type="text" /> ...
<button #click.prevent="doSubmit">Submit</button>
</form>
</contact-form>