Vue component save data for later - vue.js

I'm trying to store data for later use in vue component.
Data function, example 1
data:
function () {
return {
username: '',
phoneNumber: '',
}
}
Save method
var x = JSON.stringify(this.$data)
localStorage.setItem('xxx', x);
Load will fail , code:
var x = JSON.parse(localStorage.getItem('xxx'));
this.$data = x; // <<< Not working
When i will change Data function (add container)
data:
function () {
return {
container:{
username: '',
phoneNumber: '',
}
}
}
Load works
var x = JSON.parse(localStorage.getItem('xxx'));
this.$data.container = x.container; // <<< Works
How to not add additional container like in first example

You can't replace $data in the way you're attempting to. Instead, try taking advantage of Object.assign():
var x = JSON.parse(localStorage.getItem('xxx'));
Object.assign(this.$data, x);
This should effectively "merge" the data from x into this.$data, where any properties that match in both objects will have the values in x overwrite the values in this.$data.

Related

How to pass an array values from one function to another function in vuejs?

I am trying to get the array values from
"validateBeforeSubmit" function to "saveForm" function. But I am
getting values of "undefined" in "arrlength". Please help me to solve.
This my code in vue.js
export default {
name: '',
data() {
return {}
},
ready: function() {
this.validateBeforeSubmit()
this.saveForm();
},
methods: {
validateBeforeSubmit() {
var fieldsVal = new Array();
var firstName = document.getElementById('firstName').value
var lastName = document.getElementById('lastName').value
var designation = document.getElementById('designation').value
if (firstName != "" && lastName != "" && designation != "") {
fieldsVal.push(firstName);
fieldsVal.push(lastName);
fieldsVal.push(designation);
return fieldsVal;
} else {
fieldsVal.length = 0;
return fieldsVal;
}
return fieldsVal;
},
saveForm() {
var fieldsValArray = this.validateBeforeSubmit();
var arrLength = fieldsValArray.length;
}
}
}
I can see multiple issues in your code:
1) Don't apply jQuery-like approach for getting input values. Use v-model instead. This will simplify your code
<template>
<input v-model="form.firstName" type="text"/>
</template>
<script>
export default {
data: {
form: {
firstName: '',
}
},
methods: {
validateBeforeSubmit() {
// take `firstName` directly from `data` not need for `getElementById`
const firstName = this.form.firstName;
}
},
}
</script>
2) Remove validateBeforeSubmit and saveForm from ready. Ready hook is obsolete in vue#2. And also it makes no sense. It's better to call it on form #submit.
3) It's better to create array using [] syntax instead of new Array()
Why never use new Array in Javascript
4) Always provide name for your component for easier debug
export default {
name: 'ValidationForm',
}
5) I don't know where was an issue but it works. Check this link below. I have updated your code. Try to submit form and check the console:
https://codesandbox.io/s/w6jl619qr5?expanddevtools=1&module=%2Fsrc%2Fcomponents%2FForm.vue

Cannot format or transform data before save, bound too tightly to the view

I have some data in vuejs that I want to format before sending it off through an ajax call but it changes the view its bound to. For example I have a birthday field that is formatted like this on the view 01/11/1981 but I need to format that to YYYY-MM-DD HH:mm:ss for the db and I don't want to do this on the backend.
Where and when would I do this on the frontend? I have tried doing this before the ajax request and it changes the view, so I made a copy of the data and modified it and that also changed the view. It seems no matter what I do it affects the view.
Here is my methods block:
methods: {
/**
* Update the user's contact information.
*/
update() {
/*Attempt to copy and format*/
var formattedForm = this.form;
formattedForm.birthday = moment(formattedForm.birthday).format('YYYY-MM-DD HH:mm:ss');```
Spark.put('/settings/contact', formattedForm)
.then(() => {
Bus.$emit('updateUser');
});
},
}
Here is my data block as well:
data() {
return {
form: $.extend(true, new SparkForm({
gender: '',
height: '',
weight: '',
birthday: '',
age: '',
}), Spark.forms.updateContactInformation),
};
},
The easiest way is to make a clone using Object.assign, like so:
let form = Object.assign({}, this.form);
form.age = 21;
Here's the JSFiddle: https://jsfiddle.net/y51yuf05/
Objects are passed by reference in javascript, which means:
let a = {
"apple": 6
}
let b = a
then, b and a are pointing to the same location in the memory, it is essentially copying the address of the object in a to the variable b.
You need to therefore clone the object, there are many ways to do it like:
b = Object.assign({}, a)
MDN: Object.assign()
this would not be deeply cloned, which means if your object is nested then the nested objects would still be linked between the original and the copy.
for which I use:
function isObject(obj) {
return typeof obj === 'object' && !Array.isArray(obj)
}
function clone(obj) {
let result = {}
for (let key in obj) {
if (isObject(obj[key])) {
result[key] = clone(obj[key])
} else {
result[key] = obj[key]
}
}
return result
}
function logger () {
console.log("p.a.b.c: ", p.a.b.c)
console.log("q.a.b.c:", q.a.b.c)
console.log("r.a.b.c:", r.a.b.c)
}
let p = {a: {b: {c: 5}}}
let q = clone(p)
let r = Object.assign({}, p)
logger()
p.a.b.c = 11
logger()

Vuejs2 component not updating as parent data changes

I am trying to make a simple product catalogue using vuejs2.
All my data is stored in an object array, I then have a subset of this data which is what the product catalogue uses to display each product.The subset is used for pagination.
When a user switches pages it clears and pushes the next set of data into the array.
This automatically makes the product component display the new data.
I have issues with the following, each product has multiple colours which are stored as a comma delimited string eg (white, blue, red)
I am trying to make that information appear as a drop down list of options beside each product.
This works until I switch to the next page of data, all other details update except the colour drop downs, which only reflect the previous set of data.
My product list is stored in an object array like:
var obj = {
productID: productID,
product: title,
gender: gender,
colour: colour,
cost: cost,
size: size,
description: description
}
productArray.push(obj);
I then have several components that display this array of data:
Vue.component('product-list', {
template: '<ul id="productList">'+
'<product :productID="product.productID" v-for="product in products">'+
'<h4>Colour</h4><colourSelect :colours="product.colour" :productID="product.productID"></colourSelect>' +
'<h4>Gender</h4><span class="genderSpan"><p v-bind:id="getID(product.productID)">{{product.gender}}</p></span>' +
'</product></ul>',
data: function() {
return {
products:
paginatedArray
};
},
Vue.component('colourSelect', {
props: ['productID', 'colours'],
template: '<select v-bind:id="getID()" class="form-control input-xs"><colourOption v-for="colourItem in colourArray"></colourOption></select>',
data: function () { //split string based into array
var newArray = [];
var optionsArray = this.colours.split(',');
for (i = 0; i < optionsArray.length; i++) {
var obj = {
colour: optionsArray[i]
}
newArray.push(obj)
}
return {
colourArray: newArray
};
},
methods: {
getID: function (test) {
return 'colourSelect' + this.productID;
}
}
});
Vue.component('colourOption', {
props:['options'],
template: '<option><slot></slot></option>'
});
Within the app section of vuejs I have the following methods that do the pagination:
buildPages: function () {
for (i = 1; i < this.listLength() /this.totalPage ; i++) {
this.pages.push(i);
}
var page = this.currentPage * this.totalPage;
for (i = page; i < page + this.totalPage ; i++) {
paginatedArray.push(productArray[i]);
}
},
listLength: function () {
var listTotal = productArray.length;
return listTotal
},
changePage: function (number) {
this.currentPage = number
var page = this.currentPage * this.totalPage;
//paginatedArray = [];
var count = 0;
for (i = page; i < page + this.totalPage ; i++) {
if (typeof productArray[i] !== 'undefined') {
paginatedArray.splice(count, 1, productArray[i])
}
count++
}
},
productArray is the main array storing data, paginatedArray is the subset of data that the product component works off.
The issue appears to be within the colourSelect component, within its "data" section it splits the colour data and returns it as an colourOption component into the select, but won't update when the paginatedArray changes.
The colourSelect component does however appear to actually get passed the correct data, as getID method updates correctly. Its just the data section which is not being re-rerun.
This is my first vuejs site, anyone have any ideas around this?
Thanks
You should make the colourArray as a computed property, as the data block of component gets executed only once and later change of props will not update colourArray .
Vue.component('colourSelect', {
props: ['productID', 'colours'],
template: '<select v-bind:id="getID()" class="form-control input-xs"><colourOption v-for="colourItem in colourArray"></colourOption></select>',
computed:{
colourArray: function () { //split string based into array
var newArray = [];
var optionsArray = this.colours.split(',');
for (i = 0; i < optionsArray.length; i++) {
var obj = {
colour: optionsArray[i]
}
newArray.push(obj)
}
return newArray
}
},
methods: {
getID: function (test) {
return 'colourSelect' + this.productID;
}
}
});

How to set all object properties to null in JavaScript?

I am using Vue and suddenly some of the computed css using vuetify is not working.
The way I declare an object is
personal_info : {}
and in my template, I could just do personal_info.name and other more in every v-model of text input.
I have no errors but suddenly the vuetify has a class called input-group--dirty that will elevate the label of the text input whenever it's not empty. But suddenly, it's not working. It looks like this:
As you can see, the text and label are overlapping.
The only thing that make it work is to set the property to null which is:
personal_info : {
name: null
}
The problem is that I have hundreds of text inputs and I dont want to set everything to null.
Is there a simple way to set all of the object's properties to null instead of coding it 1 by 1?
checkout this snippet
var personal_info = {
name: 'john',
email: 'john#moto.com',
phone: 9876543210
}
console.log(JSON.stringify(personal_info)); //before looping
for (var key in personal_info ) {
personal_info[key] = null;
}
console.log(JSON.stringify(personal_info));//after looping and setting value to 'null'
Vilas example is ok. But in case you have nested properties and your obj looks like this you could try my snippet
var obj = {
a: 1 ,
b: 2,
c: {
e:3,
b: {
d:6,
e: ['23']
}
}
};
var setProps = function(flat, newVal){
for(var i in flat){
if((typeof flat[i] === "object") && !(flat[i] instanceof Array)){
setProps(flat[i], newVal);
return;
} else {
flat[i] = newVal;
}
}
}
setProps(obj, null);
console.log(JSON.stringify(obj));
you can use simple immutable oneliner:
Object.fromEntries(Object.entries(YOUR_OBJECT).map(([key]) => [key, null])))
const bio = {
name: 'john',
age: 22,
hobbies: ['soccer']
}
const resetBio = Object.fromEntries(Object.entries(bio).map(([key]) => [key, null]))
console.log(bio)
console.log(resetBio)

odoo, how to reload widget on every db record form view?

Hi this question is related with my own answer here the thing is that the widget run only once, when the first database record object is show on the form view, but when I change to another record, the view its not updated with the actual record. I think that is because I run all the code in the ´start´ but I dont know how and where put he code to do this.
the code again:
(function (instance) {
var _t = instance.web._t,
_lt = instance.web._lt;
var QWeb = instance.web.qweb;
openerp.chess_base = function (instance, local) {
local.ShowBoard = instance.web.form.FormWidget.extend({
start: function () {
this.$el.append('<div id="board" style="width: 300px">BOARD GOES HERE</div>');
this.show_board();
},
show_board: function () {
var Game = new instance.web.Model("chess.game"),
record_id = this.field_manager.datarecord.id,
record_name = this.field_manager.datarecord.name,
self = this;
self.el_board = self.$('#board');
Game.query(['pgn']).filter([['id', '=', record_id], ['name', '=', record_name]]).all().then(function (data) {
console.log(data);
self.cfg = {
position: data[0].pgn,
orientation: 'white',
pieceTheme: '/chess_base/static/img/chesspieces/wikipedia/{piece}.png'
};
ChessBoard(self.el_board, self.cfg);
});
}
});
instance.web.form.custom_widgets.add('board', 'instance.chess_base.ShowBoard');
}
})(openerp);
In your start function:
this._ic_field_manager.on('view_content_has_changed', this, function() {
'put your code here'
})
EDIT:
Actually there's another event you could listen, 'load_record'.
Because 'view_content_has_changed' is triggered every time a single field in the view is modified, and you maybe don't want this behavior.