Cannot format or transform data before save, bound too tightly to the view - vue.js

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()

Related

Calling to a method from ForEach doesn't working properly - vue.js

I'm trying to call a method from a foreach in vue, but the method is performed only one time,
No matter how many variables are in the list.
attached here the two functions I used:
CleanChips() {
this.chips.forEach((item) => {
this.RemoveRequirement(item)
})
},
RemoveRequirement(item) {
var index = this.chips.indexOf(item);
if (index > -1) {
this.chips.splice(index, 1);
this.chips = [...this.chips];
}
},
The RemoveRequirement function is performed only one time.
Any idea what's wrong here?
You modify the array itself in RemoveRequirement while running through it in CleanChips. You should create a copy of the original array to iterate in order to safely delete elements in the original array.
CleanChips() {
const chipsCopy = [...this.chips]
chipsCopy.forEach((item) => {
this.RemoveRequirement(item)
})
},
RemoveRequirement(item) {
var index = this.chips.indexOf(item);
if (index > -1) {
this.chips.splice(index, 1);
// this.chips = [...this.chips]; // you don't need this line because `splice` is reactivity-compatible.
}
},

How to update all attributes of a vue 'observer' with another 'observer'

I have two vue 'observer' data a and b with the same key value, and I need to replace the value of b with the value of a. If the value of b contains a complex object, I want this complex object to be the value of a. Deep copy copy replacement. I have some ideas: this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 }), but then someObject becomes unobservable. Do you have any good methods? thank you very much
https://codepen.io/yakiler/project/editor/DadznN#
var app = new Vue({
el: '#demo',
mounted: function () {
},
methods: {
clickMe(){
let pureData = {...this.copyData.v }; // get a pure data by vue Observer data
let f = v => v && JSON.parse(JSON.stringify(v)) || {};
let needUpdateData = this.originData.v;
let returnValue = Object.assign({}, needUpdateData, f(pureData)); // exec replace value
console.log('pureData',pureData);
console.log('needUpdateData',needUpdateData);
console.log('returnvalue',returnValue);
console.log('originData',this.originData);
}
},
data: function() {
return {
originData:{
v:{a:1},b:2
},
copyData:{
v:{a:0},b:0
}
}
}
})
I would persevere with Object.assign(). You should be able to manage the observers without too much difficulty. The easiest solution is to not have someObject at the root of your component. Create a wrapper for someObject, then when the new value is assigned, the observers will automatically be re-created.
If this is not clear, post some code :-)

Vue component save data for later

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.

Using map to reduce in Gun

I am new to Gun. I have existing code that very effectively reduces an array of objects based on a pattern. I am thinking I should tweak this to run in the context of Gun's .map and return undefined for non-matches. I think I will also have to provide two arguments, one of which is the where clause and the other the properties I want shown on returned objects. I also presume that if I use .on future matches will automagically get spit out! Am I on the right path?
const match = (object,key,value) => {
const type = typeof(value);
if(value && type==="object") {
return Object.keys(value).every(childkey =>
match(object[key],childkey,value[childkey]));
if(type==="function") return value(object[key]);
return object[key]===value;
}
const reduce = (objects,where) => {
const keys = Object.keys(where);
return objects.reduce((accumulator,current) => {
if(keys.every(key => match(current,key,where[key]))) {
accumulator.push(current);
}
return accumulator;
},[]);
}
let rows = reduce([{name: "Joe",address:{city: "Seattle"},age:25},
{name: "Mary",address:{city: "Seattle"},age:16},
{name: "Joe",address:{city: "New York"},age:20}],
{name: () => true,
address: {city: "Seattle"},
age: (age) => age > 10});
// results in
[{name: "Joe",address:{city: "Seattle"},age:25},
{name: "Mary",address:{city: "Seattle"},age:16}]
Further exploration of this resulted in the code below, which is stylistically different, but conforms to the immediate responsive nature of Gun. However, it is unclear how to deal with nested objects. The code below only works for primitives.
const match = (object,key,value) => {
const type = typeof(value);
if(!object || typeof(object)!=="object") return false;
if(value && type==="object") {
const child = gun.get(object[key]["#"]);
for(let key in value) {
const value = {};
child.get(key).val(v => value[key] = v,{wait:0});
if(!match(value,key,value[key])) return;
}
}
if(type==="function") return value(object[key]);
return object[key]===value;
}
const gun = Gun(["http://localhost:8080/gun"]),
users = [{name: "Joe",address:{city: "Seattle"},age:25},
{address:{city: "Seattle"},age:25},
{name: "Mary",address:{city: "Seattle"},age:16},
{name: "Joe",address:{city: "New York"},age:20}];
//gun.get("users").map().put(null);
for(let user of users) {
const object = gun.get(user.name).put(user);
gun.get("users").set(object);
}
gun.get("users").map(user => {
const pattern = {name: (value) => value!=null, age: (age) => age > 20}; //, address: {city: "Seattle"}
for(let key in pattern) {
if(!match(user,key,pattern[key])) return;
}
return user;
}).on(data => console.log(data));
Yes. GUN's .map method does more than what it seems.
Say we have var users = gun.get('users'). We can do:
users.map() with no callback acts like a forEach because the default callback is to return the data as-is.
users.map(user => user.age * 2) with a callback, it lets you transform the data like you would expect from a map, except where:
users.map(function(){ return }) if you return undefined, it will filter out that record.
WARNING: As of the current time, .map(transform) function is currently experimental and my have bugs with it. Please try it and report any you find.
Now we can combine it with some other methods, to get some cool behavior:
users.map().on(cb) will get current and future users as they are added to the table, and gets notified for updates on each of those users.
users.map().val(cb) will get current and future users as they are added to the table, but only gets each one once.
users.val().map().on(cb) gets only the current users (not future), but gets the updates to those users.
users.val().map().val(cb) gets only the current users (not future), and only gets them once.
So yes, you are on the right track. For instance, I have a test in gun core that does this:
list.map(user => user.age === 27? user.name + "thezombie" : u).on(function(data){
// verify
});
list.set({name: 'alice', age: 27});
list.set({name: 'bob', age: 27});
list.set({name: 'carl', age: 29});
list.set({name: 'dave', age: 25});
This creates a live map that filters the results and locally (view only) transforms the data.
In the future, this is how the SQL and MongoDB Mango query extensions will work for gun.
Note: GUN only loads the property you request on an object/node, so it is bandwidth efficient. If we do users.map().get('age') it will only load the age value on every user, nothing else.
So internally, you can do some efficient checks, and if all your conditionals match, only /then/ load the entire object. Additionally, there are two other options: (1) you can use an in-memory version of gun to create server-side request-response patterns, so you can have server-side filtering/querying that is efficient. (2) if you become an adapter developer and learn the simple wire spec and then write your own custom query language extensions!
Anything else? Hit me up! More than happy to answer.
Edit: My reply in the comments, comments apparently can't have code. Here is pseudo-code of how to "build up" more complex queries, which will be similar to how SQL/Mango query extensions will work:
mutli-value & nested value matching can be "built up" from this as the base, but yes, you are right, until we have SQL/Mango query examples, there isn't a simple/immediate "out of the box" example. This is pseudo code, but should get the idea across:
```
Gun.chain.match = function(query, cb){
var gun = this;
var fields = Object.keys(query);
var check = {};
fields.forEach(function(field){
check[field] = true;
gun.get(field).val(function(val){
if(val !== query[field]){ return }
check[field] = false;
//all checks done?
cb(results)
});
});
return gun;
}
```
Solution, the trick is to use map and not val:
Gun.chain.match = function(pattern,cb) {
let node = this,
passed = true,
keys = Object.keys(pattern);
keys.every(key => {
const test = pattern[key],
type = typeof(test);
if(test && type==="object") {
node.get(key).match(test);
} else if(type==="function") {
node.get(key).map(value => {
if(test(value[key])) {
return value;
} else {
passed = false;
}
});
} else {
node.get(key).map(value => {
if(value[key]===test) {
return value;
} else {
passed = false;
}
});
}
return passed;
});
if(passed && cb) this.val(value => cb(value))
return this;
}
const gun = new Gun();
gun.get("Joe").put({name:"Joe",address:{city:"Seattle"},age:20});
gun.get("Joe").match({age: value => value > 15,address:{ city: "Seattle"}},value => console.log("cb1",value));

In an ExtJS Grid, how do I get access to the data store fields that are part of the sort set

How do I get access to the columns/datastore fields that are part of the sort set.
I am looking to modify the a grid's sort parameters for remote sorting. I need the remote sort param's sort key to match the column's field's mapping property. I need these things to happen though the normal 'column header click sorts the data' functionality.
Remote sorting and field mapping (ExtJS 4.1)
This functionality seems not to be implemented in ExtJS. Here is a solution using the encodeSorters function provided since ExtJS 4. Accessing fields map throught the model's prototype is a bit dirty but it does the job :
var store = Ext.create('Ext.data.Store', {
...,
proxy: {
...,
encodeSorters: function (sorters) {
var model = store.proxy.model,
map = model.prototype.fields.map;
return Ext.encode(Ext.Array.map(sorters, function (sorter) {
return {
property : map[sorter.property].mapping || sorter.property,
direction: sorter.direction
};
}));
}
}
});
However, it would be more relevant to override the original method :
Ext.data.proxy.Server.override({
encodeSorters: function(sorters) {
var min, map = this.model.prototype.fields.map;
min = Ext.Array.map(sorters, function (sorter) {
return {
property : map[sorter.property].mapping || sorter.property,
direction: sorter.direction
};
});
return this.applyEncoding(min);
}
});
Assuming you are using simpleSortMode, you could do something like this in your store.
listeners: {
beforeload: function( store, operation, eOpts ) {
if (store.sorters.length > 0) {
var sorter = store.sorters.getAt(0),
dir = sorter.direction,
prop = sorter.property,
fields = store.model.getFields(),
i,
applyProp = prop;
for (i = 0; i < fields.length; i++) {
if (fields[i].name == prop) {
applyProp = fields[i].mapping || prop;
break;
}
}
//clearing the sorters since the simpleSortMode is true so there will be only one sorter
store.sorters.clear();
store.sorters.insert(0, applyProp, new Ext.util.Sorter({
property : applyProp,
direction: dir
}));
}
}
},