How to keep input data after browser back using keep-alive in vuejs? - vue.js

I am a newbie to VueJs
I would like to use Vue2 to create a validation form
Index.html
<div id="app">
<form action='process.php' method="post" name="submit_form" id="submit_form" v-on:submit="validateForm">
<label for="username">Name</label>
<input type="text" name="username" v-model="username" placeholder="Username"/>
<br><br>
<input class="submit_button" name="submit_form" type="submit" value="Submit">
</form>
</div>
but When I click the previous or next page, then back to index.html form page.
The input field's data is auto-remove.
How to using keep-alive in vuejs to save user input?
Is there any simple example?
Thank you very much

When you click on the previous or next page (I think you mean the browser's arrows) the page it's reloaded, so the javascript (and vue) too. To keep the data, you must "save" the form's state. A simple solution can be to save the form object in sessionStorage and check if there is a sessionStorage Item (let's say with a key 'formData') and fill the form object with these values.
Example:
<html>
...
<body>
<div id="app">
<form action='process.php' method="post" name="submit_form" id="submit_form" v-on:submit="validateForm" v-on:change="saveFormDataState">
<label for="username">Name</label>
<input type="text" name="username" v-model="formData.username" placeholder="Username"/>
<br><br>
<input class="submit_button" name="submit_form" type="submit" value="Submit">
</form>
</div>
<script>
new Vue({
el: '#app',
data: () => ({
formData: {
username: ''
}
}),
methods: {
initFormDataState(){
const formData = JSON.parse(sessionStorage.getItem('formData') || '');
if(formData){
this.formData = formData;
}
},
saveFormDataState(){
const formData = JSON.stringify(this.formData);
sessionStorage.setItem('formData', formData);
}
},
created(){
this.initFormDataState();
}
});
</script>
</body>
</html>
Note that I have added the on-change listener to the form to save the form's state when the user focuses on another input element or presses the submit button.

Related

Binding getter array to checkbox group

I have a two page form so I am trying to mix submitting data to the server as well as making use of vuex. So on page one, I have a simple form which contains a group of checkboxes (removed layout and styling to reduce code)
<b-form #submit.stop.prevent="onSubmit">
<b-form-group>
<input v-model="$v.form.checkboxGroup.$model" type="checkbox" name="checkbox1" value="1">
<input v-model="$v.form.checkboxGroup.$model" type="checkbox" name="checkbox2" value="2">
<input v-model="$v.form.checkboxGroup.$model" type="checkbox" name="checkbox3" value="3">
</b-form-group>
<button class="btn try-btn" type="submit">Submit</button>
</b-form>
Essentially, when submitted, I send the form data to my repository so it can be saved on the backend. If this is successful, I call the following method
handleSubmitSuccess (response) {
if (response.data.action === 'next_step') {
this.$store.dispatch('createCheckboxData', this.$v.form.$model)
return
}
}
This method sets the checkbox data in my store and routes the user to the next page (removed this part). So all of this is fine, seems to work well.
So when on page two, I have a button that can take you back to page one. My idea is that if this happens, I use the previously checked data in the store to auto check the previously selected checkbox. As such, on page one I added a computed method
computed: {
checkboxData () {
return this.$store.getters.checkboxData
}
}
Now if I output checkboxData to the console, it seems to be an Observer object
[{…}, __ob__: Observer]
0:
checkboxData: Array(2)
0: "1"
1: "3"
length: 2
So the above shows that previously, the first and second checkboxes were checked.
My question is how can I now use this data to auto-check my checkboxes. I have seen some examples online, but they do not seem to work.
Thanks
The way you use Vue is a little different to me so you might have to change this but, basically, you can set your v-model to whatever array is set in the Vuex store and it will set those checkboxes to true:
new Vue({
el: "#app",
data: {
checkbox: [],
vuexData: ['1', '3']
},
mounted() {
this.checkbox = this.vuexData;
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input v-model="checkbox" type="checkbox" name="checkbox1" value="1">
<input v-model="checkbox" type="checkbox" name="checkbox2" value="2">
<input v-model="checkbox" type="checkbox" name="checkbox3" value="3">
{{ checkbox }}
</div>

VueJs v-Model Binding with Hidden Component not working

I am developing a SPA application in Vue. I have a wizard in the wizard I have a component for each step. All the components are adding at the start and their mounted and created method/event is executing when the application starts.
I am using the event bus & when I move to the next step of wizard I emit the event and catch the event on the respective step.
Note: Component is hidden at the start and when it shows it will not have the updated data or even data in input fields.
Code of the component of the 2nd step is
<template>
<section>
<div class="container">
<form class="form-horizontal">
<div class="row form-group">
<label class="col-sm-3 control-label" for="marshaCode">MARSHA Code:</label>
<div class="col-sm-4">
<input type="text" class="form-control field-ml-15" v-model="marshaCode" placeholder="Enter Marsha Code" readonly />
<input type="text" class="form-control field-ml-15":value="marshaCode" placeholder="Enter Marsha Code" readonly />
<p>{{marshaCode}}</p>
</div>
</div>
</form>
</div>
</section>
</template>
<script>
import { EventBus } from "../../shared/eventbus.js";
export default {
data() {
return {
marshaCode:"On Load Code"
}
},
mounted() {
EventBus.$on('showSurvey', () => {
this.marshaCode="On Show Code"
});
},
}
</script>
Now when this component will show the P tag will have "On Load Code" But in both the fields there will be no data.
How can I rebind, re-render or update the data?
Use vue to show/hide the component using v-if or v-show. It can't do it's job if another library is messing with the DOM.
<section v-if='showMyComponent'>
or
<div class="col-sm-4" v-if='showMyComponent'>
<input type="text" class="form-control field-ml-15" v-model="marshaCode" placeholder="Enter Marsha Code" readonly />
<input type="text" class="form-control field-ml-15":value="marshaCode" placeholder="Enter Marsha Code" readonly />
<p>{{marshaCode}}</p>
</div>
And set showMyComponent to a computed or data property. Then remove the logic to show/hide from your other library.
You can simply add this.marshaCode="On Show Code" when mounted.
mounted() {
this.marshaCode="On Show Code"
console.log(EventBus)
EventBus.$on('showSurvey', () => {
this.marshaCode="On Show Code"
});
}
An example: https://codesandbox.io/embed/vue-template-8l2lg.

Binding vue components to class name

Alright so I am trying to bind this vue components to a class name so it triggers on every element that has this class but what happens is that it only works with the first element and not with other ones
<div class="__comment_post">
<textarea></textarea>
<input type="submit" v-on:click="submitComment" /> <!-- submit comment being only triggered on this one -->
</div>
<div class="__comment_post">
<textarea></textarea>
<input type="submit" v-on:click="submitComment" />
</div>
<div class="__comment_post">
<textarea></textarea>
<input type="submit" v-on:click="submitComment" />
</div>
As you can see above, I've got 3 divs with class __comment_post so naturally submitComment should be bound to all these 3 divs but what happens is that submitComment is being triggered only on the first one
var app = new Vue({
el:".__comment_post",
data: {
comment: ""
},
methods: {
submitComment: function() {
console.log("Test");
}
}
});
Here is a little example you and others can follow in order to bind vue instance to class names.
Lets say, you would like to bind Vue to multiple existing <div class="comment"> element in HTML.
HTML:
<div class="comment" data-id="1">
<div>
<div class="comment" data-id="2">
<div>
Now, you can try the following logic/code to your example.
JS:
var comments = {
"1": {"content": "Comment 1"},
"2": {"content": "Comment 2"}
}
$('.comment').each(function () {
var $el = $(this)
var id = $el.attr('data-id')
var data = comments[id]
new Vue({
el: this,
data: data,
template: '<div class="comment">{{ content }}<div>'
})
})
I hope this will answer your question :)
The vue instance is mounted on the first found DOM element with the css selector passed to the el option. So the rest two div have no vue instances mounted on them.
So wrap your divs with a wrapper div and mount the vue instance on that wrapper
<div id="app">
<div class="__comment_post">
<textarea></textarea>
<input type="submit" v-on:click="submitComment" /> <!-- submit comment being only triggered on this one -->
</div>
<div class="__comment_post">
<textarea></textarea>
<input type="submit" v-on:click="submitComment" />
</div>
<div class="__comment_post">
<textarea></textarea>
<input type="submit" v-on:click="submitComment" />
</div>
script
var app = new Vue({
el:"#app",
data: {
comment: ""
},
methods: {
submitComment: function() {
console.log("Test");
}
}
});

Getting form data on submit?

When my form is submitted I wish to get an input value:
<input type="text" id="name">
I know I can use form input bindings to update the values to a variable, but how can I just do this on submit. I currently have:
<form v-on:submit.prevent="getFormValues">
But how can I get the value inside of the getFormValues method?
Also, side question, is there any benefit to doing it on submit rather than updating variable when user enters the data via binding?
The form submit action emits a submit event, which provides you with the event target, among other things.
The submit event's target is an HTMLFormElement, which has an elements property. See this MDN link for how to iterate over, or access specific elements by name or index.
If you add a name property to your input, you can access the field like this in your form submit handler:
<form #submit.prevent="getFormValues">
<input type="text" name="name">
</form>
new Vue({
el: '#app',
data: {
name: ''
},
methods: {
getFormValues (submitEvent) {
this.name = submitEvent.target.elements.name.value
}
}
}
As to why you'd want to do this: HTML forms already provide helpful logic like disabling the submit action when a form is not valid, which I prefer not to re-implement in Javascript. So, if I find myself generating a list of items that require a small amount of input before performing an action (like selecting the number of items you'd like to add to a cart), I can put a form in each item, use the native form validation, and then grab the value off of the target form coming in from the submit action.
You should use model binding, especially here as mentioned by Schlangguru in his response.
However, there are other techniques that you can use, like normal Javascript or references. But I really don't see why you would want to do that instead of model binding, it makes no sense to me:
<div id="app">
<form>
<input type="text" ref="my_input">
<button #click.prevent="getFormValues()">Get values</button>
</form>
Output: {{ output }}
</div>
As you see, I put ref="my_input" to get the input DOM element:
new Vue({
el: '#app',
data: {
output: ''
},
methods: {
getFormValues () {
this.output = this.$refs.my_input.value
}
}
})
I made a small jsFiddle if you want to try it out: https://jsfiddle.net/sh70oe4n/
But once again, my response is far from something you could call "good practice"
You have to define a model for your input.
<input type="text" id="name" v-model="name">
Then you you can access the value with
this.name inside your getFormValues method.
This is at least how they do it in the official TodoMVC example: https://v2.vuejs.org/v2/examples/todomvc.html (See v-model="newTodo" in HTML and addTodo() in JS)
Please see below for sample solution, I combined the use of v-model and "submitEvent" i.e. <input type="submit" value="Submit">. Used submitEvent to benefit from the built in form validation.
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="app">
<form #submit.prevent="getFormValues">
<div class="form-group">
<input type="email" class="form-control form-control-user"
v-model="exampleInputEmail"
placeholder="Enter Email Address...">
</div>
<div class="form-group">
<input type="password" class="form-control"
v-model="exampleInputPassword" placeholder="Password"> </div>
<input type="submit" value="Submit">
</form>
</div>
<script>
const vm = new Vue({
el: '#app',
methods: {
getFormValues (submitEvent) {
alert("Email: "+this.exampleInputEmail+" "+"Password: "+this.exampleInputPassword);
}
}
});
</script>
</body>
</html>
The other answers suggest assembling your json POST body from input or model values, one by one. This is fine, but you also have the option of grabbing the whole FormData of your form and whopping it off to the server in one hit. The following working example uses Vue 3 with Axios, typescript, the composition API and setup, but the same trick will work anywhere.
I like this method because there's less handling. If you're old skool, you can specify the endpoint and the encoding type directly on the form tag.
You'll note that we grab the form from the submit event, so there's no ref, and no document.getElementById(), the horror.
I've left the console.log() there to show that you need the spread operator to see what's inside your FormData before you send it.
<template>
<form #submit.prevent="formOnSubmit">
<input type="file" name="aGrid" />
<input type="text" name="aMessage" />
<input type="submit" />
</form>
</template>
<script setup lang="ts">
import axiosClient from '../../stores/http-common';
const formOnSubmit = (event: SubmitEvent) => {
const formData = new FormData(event.target as HTMLFormElement);
console.log({...formData});
axiosClient.post(`api/my-endpoint`, formData, {
headers: {
"Content-Type": "multipart/form-data",
}
})
}
</script>

Dropzone inside a html form with other form fields not working

I want to add a dropzone inside an existing form but it doesn't seem to work.
When I view the console I get error throw new Error("No URL provided"). When I click upload I get no preview either - all I get is a normal file input.
<link href="../dropzone.css" rel="stylesheet">
<form action="/" enctype="multipart/form-data" method="POST">
<input type="text" id ="Username" name ="Username" />
<div class="dropzone" id="my-dropzone" name="mainFileUploader">
<div class="fallback">
<input name="file" type="file" />
</div>
</div>
<div>
<button type="submit" id="submit"> upload </button>
</div>
</form>
<script src="../jquery.min.js"></script>
<script src="../dropzone.js"></script>
<script>
$("my-dropzone").dropzone({
url: "/file/upload",
paramName: "file"
});
</script>
No url provided error is because $("my-dropzone") is wrong instead it must be $('#mydropzone')
dropzone along with other form, yes this is very much possible, you have to post the data using the URL provided in the dropzone not in the form action. That means all your form data along with the files uploaded shall be posted back to the url provided for the dropzone. A simple untested solution is as below;
<link href="../dropzone.css" rel="stylesheet">
<form action="/" enctype="multipart/form-data" method="POST">
<input type="text" id ="Username" name ="Username" />
<div class="dropzone" id="my-dropzone" name="mainFileUploader">
<div id="previewDiv></div>
<div class="fallback">
<input name="file" type="file" />
</div>
</div>
<div>
<button type="submit" id="submitForm"> upload </button>
</div>
</form>
<script src="../jquery.min.js"></script>
<script src="../dropzone.js"></script>
<script>
$("#mydropzone").dropzone({
url: "/<controller>/action/" ,
autoProcessQueue: false,
uploadMultiple: true, //if you want more than a file to be uploaded
addRemoveLinks:true,
maxFiles: 10,
previewsContainer: '#previewDiv',
init: function () {
var submitButton = document.querySelector("#submitForm");
var wrapperThis = this;
submitButton.addEventListener("click", function () {
wrapperThis.processQueue();
});
this.on("addedfile", function (file) {
// Create the remove button
var removeButton = Dropzone.createElement("<button class="yourclass"> Remove File</button>");
// Listen to the click event
removeButton.addEventListener("click", function (e) {
// Make sure the button click doesn't submit the form:
e.preventDefault();
e.stopPropagation();
// Remove the file preview.
wrapperThis.removeFile(file);
});
file.previewElement.appendChild(removeButton);
});
// Also if you want to post any additional data, you can do it here
this.on('sending', function (data, xhr, formData) {
formData.append("PKId", $("#PKId").val());
});
this.on("maxfilesexceeded", function(file) {
alert('max files exceeded');
// handle max+1 file.
});
}
});
</script>
The script where you initialize dropzone can be inside $document.ready or wrap it as a function and call when you want to initialize it.
Happy coding!!