Retain filled form fields on page refresh in Vue - javascript

The issue I am facing here is, I am not able to figure out how can I retain the values in the form on page refresh. Each time I refresh the page all the filled values in the form are gone.
Help me resolve this issue. I was thinking of using localStorage but not sure how I can implement it.
<template>
<v-card class="mb-12">
<v-form :model='user' class="content-padding" ref='pdfInputs'>
<div class="section-header">
User
</div>
<v-container fluid>
<ul>
<li v-for="(input, index) in user.inputs">
<input type="text" v-model="input.one"> - {{ input.one }}
<input type="text" v-model="input.two"> - {{ input.two }}
<button type="button" #click="deleteRow(index)">Delete</button>
</li>
</ul>
</v-container>
</v-form>
</v-card>
</template>
<script>
export default {
data () {
return {
user: {
inputs: []
}
}
}
methods: {
addRow() {
this.user.inputs.push({
one: '',
two: ''
})
},
deleteRow(index) {
this.user.inputs.splice(index,1)
}
}
}
</script>

There is watch functionality in vue
export default {
data () {
return {
user: {
inputs: []
}
}
},
mounted() {
this.user.inputs = JSON.parse(localStorage.getItem('form')) || [];
},
watch: {
user: {
handler: function() {
localStorage.setItem('form', JSON.stringify(this.user.inputs));
},
deep: true
}
},
methods: {
addRow() {
this.user.inputs.push({
one: '',
two: ''
})
},
deleteRow(index) {
this.user.inputs.splice(index,1)
}
}
}

Related

How to pass dynamically props from one child component to another on the parent page

I have two child components I have to pass dynamically props from first child to parent and from parent to second child.
Parent
<script>
data: () => ({
model: {}
}),
methods: {
changeData(payload) {
this.model.personalData = {...payload}
}
}
</script>
<template>
<first-child #changeData="(payload) => changeData(payload)"/>
<second-child :enter-object="model" />
</template>
Child one
<script>
data: () => ({
model: {}
}),
methods: {
changeData() {
this.$emit("changeData", this.model);
}
}
</script>
<template>
<v-text-field v-model="model.name" #input="changeData()">
<v-text-field v-model="model.email" #input="changeData()">
</template>
Child two
<script>
props: {
enterObject: {
type: Object,
required: false,
default: () => ({})
}
},
data: () => ({
model: {}
}),
watch: {
enterObject: {
immediate: true,
handler() {
Object.assign(this.model.personalData, this.enterObject.personalData);
}
}
</script>
<template>
<div>
<div v-if="model.personalData.name || model.personalData.email">
<span class="mr-3">{{ model.personalData.name }}</span>
<span>{{ model.personalData.email }}</span>
</div>
<div v-else>
No data
</div>
</div>
</template>
I get data in parent component with no problem, but this data doesn't pass to second child, why I have always "No data" ?
I tested your code and found a few things:
You need to create "personalData" inside the model in "childTwo".
<template>
<div>
// I changed the validation for personalData
<div v-if="model.personalData">
<span class="mr-3">{{ model.personalData.name }}</span>
<span>{{ model.personalData.email }}</span>
</div>
<div v-else>No data</div>
</div>
</template>
export default {
props: {
enterObject: {
type: Object,
required: false,
default: () => ({})
}
},
data: () => ({
model: {
personalData: {}
}
}),
watch: {
enterObject: {
deep: true,
handler() {
// Add a validation in the handler, you can use Object assign inside the validation.
if(this.enterObject) {
Object.assign(this.model.personalData, this.enterObject.personalData)
}
}
}
}
It's worked for me.I hope it helps you.
You have to assign the value of the object using this.$set for more about object reactivity click here
your Parent component should be like this:-
here is the working example
<template>
<div>
<first-child #change-data="(payload) => changeData(payload)" />
<second-child :enter-object="model" />
</div>
</template>
<script>
import FirstChild from "./FirstChild";
import SecondChild from "./SecondChild";
export default {
data: () => ({
model: {},
compKey: 0,
}),
components: {
FirstChild,
SecondChild,
},
methods: {
changeData(payload) {
this.$set(this.model, "test", payload);
//this.model.test = payload;
},
},
};
</script>

Vue js: How to hide button

i'm new to Vue js in this code below , i wanted to hide button "Clear Filter" when nothing selected and show the button only when function " selectedAnswer(index)" called so that it will show only when filter applied otherwise it should be hided , is there a way to do it in my code?
and thanks in advance
<template>
<div class="container" width=800px>
<b-row>
<b-col cols="8">
<h1> Recently Asked </h1>
<ul class="container-question" v-for="(question1,index) in questions" :key="index">
<li>
{{question1.question}}
</li>
</ul>
</b-col>
<b-button class="outline-primaryy" style="margin:auto;" #click="ClearFilter" >Clear Filter</b-button>
</div>
<router-view />
</div>
</template>
<script>
export default {
data() {
return {
questions: [],
answered: null,
index: 0,
selectedIndex: null,
}
},
methods: {
selectedAnswer(index) {
this.selectedIndex = index;
this.questions = this.questions.filter((question) => question.incorrect_answers.includes(index))
console.log(index)
},
ClearFilter() {
this.questions = this.unmutated
},
watch: {
question1: {
handler() {
this.selectedIndex = null;
this.answered = false;
},
},
},
},
mounted: function() {
fetch('https://opentdb.com/api.php?amount=10&category=9&difficulty=medium&type=multiple', {
method: 'get'
})
.then((response) => {
return response.json()
})
.then((jsonData) => {
this.questions = jsonData.results
this.unmutated = jsonData.results;
})
}
}
</script>
You just need to add a v-if="selectedIndex" to your btn element.
ie
<b-button v-if="selectedIndex" class="outline-primaryy" style="margin:auto;" #click="ClearFilter" >Clear Filter</b-button

Passing data to the modal in Vue not working

I am trying to pass data to the UserModal. But the issue I am facing here is that the value of
user_clicked field is set when the openuserdialog method runs(checked in console: the value is assigned) but I am not able to pass it as an argument to the modal. Please help me solve the problem.
<v-data-table :items="users" :disable-initial-sort="true" :mustSort="true" hide-actions>
<template slot="items" slot-scope="props">
<td>{{ props.item.file_type.name }}</td>
<td>{{ props.item.created_at | moment }}</td>
<td><a #click="openUserDialog(props.item.id, props.item.user_type)" href='javascript:void(0);' class="details-link"><span class="hidden-xs-only">UserTypes</span><span class="hidden-sm-and-up">User Types</span></a></td>
</template>
</v-data-table>
<v-dialog v-model="userDialog" max-width="1275">
<UserModal :document="user_clicked" />
<div class="text-xs-right">
<v-btn class='vue-file-button text-right' #click="closeUserDialog" >Close</v-btn>
</div>
</v-dialog>
<script>
import UserModal from 'views/users/shortlisted_users.vue';
export default {
components: {
UserModal
},
data: function() {
return {
userDialog: false,
user_clicked: ''
}
}
methods: {
openUserDialog(document_id, user_type) {
this.userDialog = true;
this.user_clicked = user_type;
console.log(this.user_clicked);
},
closeUserDialog(document_id) {
this.userDialog = false;
}
}
</script>
Update 1
openUserDialog(document_id, user_type) {
this.user_clicked = user_type;
this.userDialog = true;
console.log(this.user_clicked);
}
Update 2
<template>
<div>
<v-card id="users-card">
<Users :users="users"></Users>
</v-card>
</div>
</template>
<script>
import 'vue-awesome/icons';
import Icon from 'vue-awesome/components/Icon';
import Users from 'views/user/_user_table.vue';
export default {
components: {
Icon,
Users
},
props: ['document'],
data: () => ({
users: [],
tab_view: 'tab-users-card'
}),
created: function() {
console.log(this.document);
this.fetchUsers(this.document);
},
methods: {
fetchUsers(document) {
this.$axios.get('/my_account/users/document_suggested_users.json', {
params: {
document: document.id
}
})
.then(response => {
this.users = response.data;
})
},
}
};
</script>
The problem is that you are trying to use document in the created handler of the component which is far too early in its life-cycle.
Instead, one approach is to use a watch handler in your UserModal like this:
watch: {
document: function () {
console.log(this.document);
if (this.document) {
this.fetchUsers(this.document);
}
}
}
Try to declare your prop like so:
props: {
document: Object
}

Click-and-edit text input with Vue

I'm looking for a click-and-edit Vue component.
I've found a fiddle and made some edits. It works like this:
The fiddle is here.
The problem: I need an additional click to make the input focused. How can I make it focused automatically?
The code from the fiddle. HTML:
<div id="app">
Click the values to edit!
<ul class="todo-list">
<li v-for = "todo in todos">
<input v-if = "todo.edit" v-model = "todo.title"
#blur= "todo.edit = false; $emit('update')"
#keyup.enter = "todo.edit=false; $emit('update')">
<div v-else>
<label #click = "todo.edit = true;"> {{todo.title}} </label>
</div>
</li>
</ul>
</div>
JS:
new Vue({
el: '#app',
data: {
todos: [{'title':'one value','edit':false},
{'title':'one value','edit':false},
{'title':'otro titulo','edit':false}],
editedTodo: null,
message: 'Hello Vue.js!'
},
methods: {
editTodo: function(todo) {
this.editedTodo = todo;
},
}
})
You can use a directive, for example
JS
new Vue({
el: '#app',
data: {
todos: [
{ title: 'one value', edit: false },
{ title: 'one value', edit: false },
{ title: 'otro titulo', edit: false }
],
editedTodo: null,
message: 'Hello Vue.js!'
},
methods: {
editTodo: function (todo) {
this.editedTodo = todo
}
},
directives: {
focus: {
inserted (el) {
el.focus()
}
}
}
})
HTML
<div id="app">
Click the values to edit!
<ul class="todo-list">
<li v-for="todo in todos">
<input
v-if="todo.edit"
v-model="todo.title"
#blur="todo.edit = false; $emit('update')"
#keyup.enter="todo.edit=false; $emit('update')"
v-focus
>
<div v-else>
<label #click="todo.edit = true;"> {{todo.title}} </label>
</div>
</li>
</ul>
</div>
You can find more info here
https://v2.vuejs.org/v2/guide/custom-directive.html
With #AitorDB's help I have written a Vue component for this, I call it Click-to-Edit. It is ready to use, so I'm posting it.
What it does:
Supports v-model
Saves changes on clicking elsewhere and on pressing Enter
ClickToEdit.vue: (vue 2.x)
<template>
<div>
<input type="text"
v-if="edit"
:value="valueLocal"
#blur.native="valueLocal = $event.target.value; edit = false; $emit('input', valueLocal);"
#keyup.enter.native="valueLocal = $event.target.value; edit = false; $emit('input', valueLocal);"
v-focus=""
/>
<p v-else="" #click="edit = true;">
{{valueLocal}}
</p>
</div>
</template>
<script>
export default {
props: ['value'],
data () {
return {
edit: false,
valueLocal: this.value
}
},
watch: {
value: function() {
this.valueLocal = this.value;
}
},
directives: {
focus: {
inserted (el) {
el.focus()
}
}
}
}
</script>
Edit for 3.x: [Breaking changes between 2.x and 3.x]
remove .native from the event handlers
change the focus hook to mounted as described in Custom Directives 3.x.
Built on #Masen Furer's work. I added some protection to handle when a user deletes all of the data. There is probably a way to accomplish this using "update" but I couldn't get it working.
I also added the ability to hit escape and abandon any changes.
<template>
<span>
<input type="text"
v-if="edit"
:value="valueLocal"
#blur="save($event);"
#keyup.enter="save($event);"
#keyup.esc="esc($event);"
v-focus=""/>
<span v-else #click="edit = true;">
{{valueLocal}}
</span>
</span>
</template>
<script>
export default {
props: ['value'],
data () {
return {
edit: false,
valueLocal: this.value,
oldValue: (' ' + this.value).slice(1)
}
},
methods: {
save(event){
if(event.target.value){
this.valueLocal = event.target.value;
this.edit = false;
this.$emit('input', this.valueLocal);
}
},
esc(event){
this.valueLocal = this.oldValue;
event.target.value = this.oldValue;
this.edit = false;
this.$emit('input', this.valueLocal);
}
},
watch: {
value: function() {
this.valueLocal = this.value;
}
},
directives: {
focus: {
inserted (el) {
el.focus()
}
}
}
}
</script>

Vue.js2 - Object.assign({}, this.var) preventing watch method

returning this.user (a global computed property) works as expected. Of course, I'm making a copy because I do not want to overwrite the actual user data. So, I'm using Object.assign. However, once I include return Object.assign({}, this.user) (opposed to this.user), the watch method no longer functions.
Here is my template (I am using bootstrap-vue):
<template>
<form role="form">
<b-form-group
label="First Name"
label-for="basicName"
:label-cols="3"
:horizontal="true">
<b-form-input id="user-name-first" type="text" v-model="userFormData.fname"></b-form-input>
</b-form-group>
<b-form-group
label="Last Name"
label-for="basicName"
:label-cols="3"
:horizontal="true">
<b-form-input id="user-name-lirst" type="text" v-model="userFormData.lname"></b-form-input>
</b-form-group>
<b-form-group
label="Email"
label-for="user-email"
:label-cols="3"
:horizontal="true">
<b-form-input id="user-email" type="text" v-model="userFormData.email"></b-form-input>
</b-form-group>
<b-form-group
:label-cols="3"
:horizontal="true">
<b-button type="submit" variant="primary">Save changes</b-button>
<b-button type="button" variant="secondary" #click="userFormCancel">Cancel</b-button>
</b-form-group>
</form>
</template>
So, this works and sets editsPending to true whenever changes are applied to userProfile (via v-model on an input)
<script>
export default {
name: 'userProfile',
data () {
return {
editsPending: false
}
},
computed: {
userFormData: function () {
return this.user
}
},
watch: {
userFormData: {
deep: true,
handler (val) {
this.editsPending = true
}
}
},
methods: {
userFormCancel () {
this.editsPending = false
}
}
}
</script>
...but this does not; userFormData becomes a clone of user but editsPending is not affected by updates to userFormData.
<script>
export default {
name: 'userProfile',
data () {
return {
editsPending: false
}
},
computed: {
userFormData: function () {
return Object.assign({}, this.user)
}
},
watch: {
userFormData: {
deep: true,
handler (val) {
this.editsPending = true
}
}
},
methods: {
userFormCancel () {
this.editsPending = false
}
}
}
</script>
Can anyone explain why this may be happening and suggest a viable solution?
A computed property will only re-evaluate when some of its
dependencies have changed. (source)
That's why it works with return this.user and not with Object.assign because it's not a reactive dependency.
If you want reactive data you should initialize userFormData as an empty object data and assign your user when your Vue instance is created:
data () {
return {
editsPending: false,
userFormData: {}
}
},
created() {
this.userFormData = Object.assign({}, this.user)
},
Tested different things to reproduce the behaviour you see.
I suspect that in your template your are binding your inputs to UserFormdata (incorrect)
<input v-model="userFormData.name">
Instead of (correct)
<input v-model="user.name">
If you could share your template that would help ;)
Edit: After template addition.
new Vue({
el: '#app',
data () {
return {
editsPending: false,
user: { name: 'John Doe' },
userCachedData: {},
}
},
created() {
this.userCachedData = Object.assign({}, this.user);
},
watch: {
user: {
deep: true,
handler (val) {
this.editsPending = true
}
}
},
methods: {
userFormCancel () {
this.editsPending = false
}
}
})
<div id="app">
{{ user }}
{{ userCachedData }}
<br>
<input v-model="user.name" />
{{ this.editsPending }}
</div>
Codepen: https://codepen.io/aurelien-bottazini/pen/BVNJaG?editors=1010
You can use the $emit to assign value to object:
mounted() {
this.$emit("userFormData", this.user);
}

Categories