How to set/get/save data in session storage? - javascript

here is example of my data that I get after ajax call response is successful obj.DATA looks like this:
{
"A43D": {
"FIRSTNAME": "Mike",
"EMAIL": "mjohns#gmail.com",
"LASTNAME": "Johns"
},
"4E83": {
"FIRSTNAME": "Steve",
"EMAIL": "scook#gmail.com",
"LASTNAME": "Cook"
}
}
$.ajax({
type: 'POST',
url: 'AjaxFunctions.cfc?method=getCustomers',
data: formData,
dataType: 'json'
}).done(function(obj){
console.log(obj.DATA);
sessionStorage.setItem("customersData", JSON.stringify(obj.DATA));
}).fail(function(jqXHR, textStatus, errorThrown){
alert('Error: '+errorThrown);
});
Then I dump sessionStorage in console.log(sessionStorage) and I see this:
{
"customersData": "{\"A43D\":{\"FIRSTNAME\":\"Mike\",\"EMAIL\":\"mjohnes#gmail.com\",\"LASTNAME\":\"Johnes\"},\"4E83\":{\"FIRSTNAME\":\"Steve\",\"EMAIL\":\"scook#gmail.com\",\"LASTNAME\":\"Cook\"}}"
}
So I was trying next:
sessionData = sessionStorage.hasOwnProperty(customersData[recordID]) ? JSON.parse(sessionStorage.getItem(customersData[recordID])) : null;
This is in the function where I just pass record id and then try to access that record in session storage. If I try to console.log(sessionData) all I see is null. I'm wondering how I can access specific key in customersData object inside of the session storage? Also how I would insert/edit record in sessionStorage customersData object?

each time you try to save/load something for the localStorage/sessionStorage and you know it is a json object, always stringify/parse it depending on the case.
here you have your code fixed to work.
NOTE: I tried to create a snippet but it didn't work because we can not access to the sessionStorage of a sandbox.
NOTE2: always check what data are you going to parse, if the record doesnt exist on the storage, it will return null.
var data = {
"A43D": {
"FIRSTNAME": "Mike",
"EMAIL": "mjohns#gmail.com",
"LASTNAME": "Johns"
},
"4E83": {
"FIRSTNAME": "Steve",
"EMAIL": "scook#gmail.com",
"LASTNAME": "Cook"
}
}
//here we save the item in the sessionStorage.
sessionStorage.setItem("customersData", JSON.stringify(data));
//now we retrieve the object again, but in a string form.
var customersDataString = sessionStorage.getItem("customersData");
console.log(customersDataString);
//to get the object we have to parse it.
var customersData = JSON.parse(customersDataString);
console.log(customersData);

Here's how I'd approach this,
by creating some customersStorage Object with methods like get, set and add
jsFiddle demo
var customersStorage = {
// Get all or single customer by passing an ID
get: function( id ) {
var parsed = JSON.parse(sessionStorage.customersData || "{}");
return id ? parsed[id] : parsed;
},
// Set all
set: function( obj ) {
return sessionStorage.customersData = JSON.stringify(obj || {});
},
// Add single customer and store
add: function( id, obj ) {
var all = this.get();
// Make sure customer does not exists already;
if(all.hasOwnProperty(id)) return console.warn(id+ " exists!");
all[id] = obj;
return this.set(all);
}
};
// Let's play!
// Store All
customersStorage.set({
"A43D": {
"FIRSTNAME": "Mike",
"EMAIL": "mjohns#gmail.com",
"LASTNAME": "Johns"
},
"4E83": {
"FIRSTNAME": "Steve",
"EMAIL": "scook#gmail.com",
"LASTNAME": "Cook"
}
});
// Get single
console.log( customersStorage.get("4E83") );
// Add new
customersStorage.add("AAA0", {
"FIRSTNAME": "Espresso",
"LASTNAME": "Coffee",
"EMAIL": "espresso#coffee.com"
});
// Get all
console.log( customersStorage.get() );
Furthermore, to make your session handling object more "client" agnostic I'd expand it to handle even more data by namespace:
var ObjectStorage = function(nsp, useSession) {
var stg = (useSession ? sessionStorage : localStorage);
return {
// Get all or single customer by passing an ID
get: function(id) {
var parsed = JSON.parse(stg[nsp] || "{}");
return id ? parsed[id] : parsed;
},
// Set all
set: function(obj) {
return stg[nsp] = JSON.stringify(obj || {});
},
// Add one to all
add: function(prop, val) {
var all = this.get();
// Make sure property does not exists already;
if (all.hasOwnProperty(prop)) return console.warn(prop + " exists!");
all[prop] = val;
return this.set(all);
}
}
};
// Let's play!
// Create instance. Use true to use sessionStorage (instead of localStorage)
var Customers = new ObjectStorage("customersData", true);
// now you can use .add(), .get(), .set() on your Customers Object

Related

Dynamic Item ViewModel Knockout

I have a lot of (KnockOut) view models that get data from a rest service and then populate "item" view models that are pretty much simple and just contain the fields coming from the REST interface.
I was just wondering if there was a way to not having to define the item viewmodels but somehow just create them dynamic as objects (where each property is an observable).
So in the example below I would want to not have the "ItemViewModel" but just say within the AddItems function that it should create an object based on the data and make each entry an ko.observable. the passed "itemName" then contains "ItemViewModel1" (or in other call "ItemViewModel2" ...etc).
So e.g. if the Json Rest input has a field "LAST_NAME" it would add self.LAST_NAME = ko.observable()" filled with that value etc. (so I can still reference it in the views).
var ItemViewModel1 = function (data) {
var self = this;
self.PAR1 = ko.observable(data.PAR1)
self.PAR2 = ko.observable(data.PAR2)
self.PAR3 = ko.observable(data.PAR3)
self.PAR4 = ko.observable(data.PAR4)
// … etc
}
var MasterViewModel1 = function (data) {
var self = this;
ReportBaseViewModel.call(self)
}
var ReportBaseViewModel = function () {
var self = this;
/* commonly used vars */
self.report = ko.observable();
self.searchedCallBackFunction = ko.observable();
self.items = ko.observableArray();
self.selecteditem = ko.observable();
self.selectedPerson = ko.observable();
/* method: print */
self.PrintEventHandler = function (data) { window.print(); };
/* method: add items to array */
self.AddItems = function (data) {
var newitems = ko.utils.arrayMap(data, function (item) {
c = new window[self.itemname](item);
return c;
});
self.items(newitems);
};
/* eventhandler: select one item */
self.SelectEventHandler = function (item) {
selecteditem(item);
};
self.GetReport = function (selectedPerson, viewContainer, url, itemName) {
self.selectedPerson(selectedPerson);
self.itemname = itemName;
var jqxhr = $.ajax({
url: url,
type: "GET"
}).done(function (data, textStatus, jqXHR) {
if (data != null) {
self.AddItems(data);
$('#' + viewContainer).show();
document.getElementById(viewContainer).scrollIntoView();
}
}).fail(function (jqXHR, textStatus, errorThrown) {
console.log('fail' + JSON.stringify(jqXHR));
toastr.options = {
"closeButton": true,
"debug": false,
"newestOnTop": false,
"progressBar": false,
"positionClass": "toast-top-right",
"preventDuplicates": false,
"onclick": null,
"showDuration": "0",
"hideDuration": "1000",
"timeOut": "0",
"extendedTimeOut": "0",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
};
toastr["error"]("ERROR");
}).always(function (jqXHR, textStatus, errorString) {
if (typeof self.searchedCallBackFunction() === 'function') {
self.searchedCallBackFunction();
}
});
}
}
There is. If your objects are simple and not nested, you can write the code to map them yourself:
var someJSON = '{ "firstName": "John", "lastName": "Doe" }';
var makeSimpleVM = function(obj) {
// Return a new object with all property
// values wrapped in an observable
return Object
.keys(obj)
.reduce(function(vm, key) {
vm[key] = ko.observable(obj[key]);
return vm;
}, {});
};
var myVM = makeSimpleVM(JSON.parse(someJSON));
console.log(ko.isObservable(myVM.firstName)); // true
console.log(myVM.firstName()); // John
myVM.firstName("Jane");
console.log(myVM.firstName()); // Jane
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
I think it's important to read through this naive implementation: it makes you understand why it's probably a better idea to use a ready-made plugin.
As soon as your server side code contains arrays, nested viewmodels or any properties that you don't want mapped, you'll run in to problems. The ko.mapping plugin has already solved these problems for you. It maps arrays to ko.observableArrays and lets you specify mapping strategies.
var someJSON = '{ "firstName": "John", "lastName": "Doe" }';
// Let's use the library this time
var myVM = ko.mapping.fromJS(JSON.parse(someJSON));
console.log(ko.isObservable(myVM.firstName)); // true
console.log(myVM.firstName()); // John
myVM.firstName("Jane");
console.log(myVM.firstName()); // Jane
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
You could try using the mapping plugin or the Json functions, depending on what exactly you are looking for. I think what you are looking for is the mapping plugin:
http://knockoutjs.com/documentation/plugins-mapping.html
http://knockoutjs.com/documentation/json-data.html

Search for a related json data

How can i find data that is related to the already known data?
( I'm a newb. )
For example here is my json :
[
{ "id": "1", "log": "1","pass": "1111" },
{ "id": 2, "log": "2","pass": "2222" },
{ "id": 3, "log": "3","pass": "3333" }
]
Now i know that "log" is 1 and i want to find out the data "pass" that is related to it.
i've tried to do it so :
The POST request comes with log and pass data , i search the .json file for the same log value and if there is the same data then i search for related pass
fs.readFile("file.json", "utf8", function (err, data) {
var jsonFileArr = [];
jsonFileArr = JSON.parse(data); // Parse .json objekts
var log = loginData.log; // The 'log' data that comes with POST request
/* Search through .json file for the same data*/
var gibtLog = jsonFileArr.some(function (obj) {
return obj.log == log;
});
if (gotLog) { // If there is the same 'log'
var pass = loginData.pass; // The 'pass' data that comes with POST request
var gotPass = jsonFileArr.some(function (obj) {
// How to change this part ?
return obj.pass == pass;
});
}
else
console.log("error");
});
The problem is that when i use
var gotPass = jsonFileArr.some(function (obj) {
return obj.pass == pass;
});
it searches through the whole .json file and not through only one objekt.
Your main problem is that .some() returns a boolean, whether any of the elements match your predicate or not, but not the element itself.
You want .find() (which will find and return the first element matching the predicate):
const myItem = myArray.find(item => item.log === "1"); // the first matching item
console.log(myItem.pass); // "1111"
Note that it is possible for .find() to not find anything, in which case it returns undefined.
The .some() method returns a boolean that just tells you whether there is at least one item in the array that matches the criteria, it doesn't return the matching item(s). Try .filter() instead:
var jsonFileArr = JSON.parse(data);
var log = loginData.log;
var matchingItems = jsonFileArr.filter(function (obj) {
return obj.log == log;
});
if (matchingItems.length > 0) { // Was at least 1 found?
var pass = matchingItems[0].pass; // The 'pass' data that comes with the first match
} else
console.log("error"); // no matches
Using ES6 Array#find is probably the easiest, but you could also do (among other things)
const x = [{
"id": "1",
"log": "1",
"pass": "1111"
}, {
"id": 2,
"log": "2",
"pass": "2222"
}, {
"id": 3,
"log": "3",
"pass": "3333"
}];
let myItem;
for (let item of x) {
if (item.log === '1') {
myItem = item;
break;
}
}
console.log(myItem);

Meteor Wrapasync server side save api call result to collection

I am trying to insert some data in a collection after an asynchronous api call in the Accounts.onCreateUser callback
(API: https://github.com/Mangopay/mangopay2-nodejs-sdk).
However I am getting the error
throw new Error("Meteor code must always run within a Fiber. " +
Error: Meteor code must always run within a Fiber. Try wrapping
callbacks that you pass to non-Meteor libraries with
Meteor.bindEnvironment.
Here's my first try:
Accounts.onCreateUser(function(options, user) {
mangoPayApi.Users.create({
"Email": options.email,
"FirstName": options.profile.firstName,
"LastName": options.profile.lastName,
"CountryOfResidence": "FR",
"Nationality": "FR",
"Birthday": new Date(aBirthDateTimestamp).getTime() / 1000,
"PersonType": "NATURAL",
"Tag": user._id,
}, function(mpUser) {
// User created - using callback
console.log('User created ');
console.log(mpUser);
aMPData.user = mpUser;
MPData.insert(aMPData); // Insert data into collection
Second shot:
I tried to make the api call aynchronous
let synCreateUser = Meteor.wrapAsync(mangoPayApi.Users.create, mangoPayApi.Users );
user = synCreateUser.create({
"Email": post.emails[0],
"FirstName": post.profile.firstName,
"LastName": post.profile.lastName,
"CountryOfResidence": "FR",
"Nationality": "FR",
"Birthday": new Date(aBirthDateTimestamp).getTime() / 1000,
"PersonType": "NATURAL",
"Tag": post._id,
});
But now I get the following error
Exception in queued task: TypeError: Object function (/* arguments */) {
var self = context || this;
var newArgs = _.toArray(arguments);
var callback;
for (var i = newArgs.length - 1; i >= 0; --i) {
var arg = newArgs[i];
var type = typeof arg;
if (type !== "undefined") {
if (type === "function") {
callback = arg;
}
break;
}
}
if (! callback) {
if (Meteor.isClient) {
callback = logErr;
} else {
var fut = new Future();
callback = fut.resolver();
}
++i; // Insert the callback just after arg.
}
newArgs[i] = Meteor.bindEnvironment(callback);
var result = fn.apply(self, newArgs);
return fut ? fut.wait() : result;
} has no method 'create'
at Object.added (server/main.js:102:30)
at [object Object].observeChangesCallbacks.added (packages/minimongo/observe.js:153:1)
at self.applyChange.added (packages/minimongo/observe.js:53:1)
How can I insert the data I get from an api call into a collection ?
Thanks !
I am not sure what is handleCharge, but basically you should call synCreateUser if you'd like to use synchronous function you created at previous line:
let synCreateUser = Meteor.wrapAsync(mangoPayApi.Users.create, mangoPayApi.Users);
user = synCreateUser({
"Email": post.emails[0],
"FirstName": post.profile.firstName,
"LastName": post.profile.lastName,
"CountryOfResidence": "FR",
"Nationality": "FR",
"Birthday": new Date(aBirthDateTimestamp).getTime() / 1000,
"PersonType": "NATURAL",
"Tag": post._id,
});
// now you can process result from user variable
/* Create MangoPay user */
mangoPayApi.Users.create({
"Email": document.emails[0].address,
"FirstName": document.profile.firstName,
"LastName": document.profile.lastName,
"CountryOfResidence": "FR",
"Nationality": "FR",
"Birthday": new Date(aBirthDateTimestamp).getTime() / 1000,
"PersonType": "NATURAL",
"Tag": document._id,
}, Meteor.bindEnvironment( function(mpUser) {
// User created - using callback
console.log('User created ');
console.log(mpUser);
/* Mangopay Id */
aMPData.Id = mpUser.Id;
if(mpUser)
{
mangoPayApi.Wallets.create({
"Owners": [mpUser.Id],
"Description": "Client Wallet",
"Currency": "EUR"
}, Meteor.bindEnvironment( function(clientWallet) {
console.log('wallet created');
console.log(clientWallet);
aMPData.clientWallet.Id = clientWallet.Id;
aMPData.clientWallet.Owner = clientWallet.Owners[0];
/* MangoPay clientWallet wallet created */
if(clientWallet)
{
mangoPayApi.Wallets.create({
"Owners": [clientWallet.Owners[0]],
"Description": "mw Wallet",
"Currency": "EUR"
}, Meteor.bindEnvironment(function(mw) {
if(mw)
{
console.log(mw);
aMPData.mw.Id = mw.Id;
aMPData.mw.Owner = mw.Owners[0];
// Mangopay.insert(aMPData);
Meteor.users.update(document._id, { $set: { mangopay: aMPData } });
}
}));
}
})); // callback mangoPayApi.Wallets // Meteor.bindEnvironment callback angoPayApi.Wallets // mangoPayApi.Wallets.create
} // end if mpUser
})); // callback Users.create // Meteor.bindEnvironment callback Users.create// mangoPayApi.Users.create;
}

IndexedDB and Javascript: JSON and objects misunderstanding

I'm trying to obtain information from a JSON file download to the client through AJAX and I'm getting different results depending on the JSON format and I don't know how to fix the one with problem.
First case:
The json files looks like:
[{"name": "nick",
"age": 28},
{"name": "katie",
"age": 32}]
My AJAX .done method looks like:
.done(
function(data) {
addObjectsDB (data, "people");
})
This method calls a second one that iterates through data and stored correctly each object into IndexedDB.
Second case:
Now I have a JSON file with different format:
[
{
"husband": {
"name": "Jhon",
"age": 23 },
"wife": {
"name": "Marie",
"age": 24 }
}
]
Now my .done() AJAX method iterates through data and add each person, husband or wife to an array which is then sent to the DB with the same method than the first case:
.done(
function(data) {
var people = [];
$(data).each(function (key, value){
people.push(value.husband);
people.push(value.wife);
});
addObjectsDB (people, "people");
})
In this case the insertion into the database fails, if for example, instead of adding value.husband to people array I just add value to people array the insertion works, but I need each person stored separated in the DB.
The addObjectsDB method is:
function addObjectsDB (data, collection) {
var objectStore = db.transaction(collection, "readwrite").objectStore(collection);
$.each (data, function (key, value) {
var request = objectStore.add(value);
});
}
As I said the first case works perfectly but the second one inserts nothing and no error is showed...
I think the problem is that I don't understand javascript types adequately but I'm starting with it and I've spent a whole evening with it.
There's nothing wrong with your IDB code. Look for your answer in the code you haven't presented, particularily the AJAX response (is your JSON parsed the way you think it is?)
Be sure to attach event listeners for the error event. I'm positive that if your IDB "inserts nothing" then in fact it's not true that "no error is showed" and rather no error is seen due to callback mismanagement.
Here's a working implementation, modified from a previous answer I've given on this tag. This implementation doesn't have the uniqueness constraints you've put on your schema on purpose: it shows that your looping is fine. The entries below all look good.
var db_name = 'SO_22977915',
store_name = 'people';
$.ajax({
url: '/echo/json/',
type: 'post',
dataType: 'json',
data: {
json: JSON.stringify({
case1: [{
"name": "nick",
"age": 28
}, {
"name": "katie",
"age": 32
}],
case2: [{
"husband": {
"name": "Jhon",
"age": 23
},
"wife": {
"name": "Marie",
"age": 24
}
}]
})
},
success: function (data) {
var request,
upgrade = false,
doTx = function (db, entry) {
addData(db, entry, function () {
getData(db);
});
},
getData = function (db) {
db.transaction([store_name], "readonly").objectStore(store_name).openCursor(IDBKeyRange.lowerBound(0)).onsuccess = function (event) {
var cursor = event.target.result;
if (null !== cursor) {
console.log("entry", cursor.value);
cursor.continue();
}
};
},
addData = function (db, entry, finished) {
console.log('adding', entry);
var tx = db.transaction([store_name], "readwrite"),
people = [];
tx.addEventListener('complete', function (e) {
finished();
});
$.each(entry.case1, function (key, value) {
tx.objectStore(store_name).add(value);
});
$(entry.case2).each(function (key, value){
people.push(value.husband);
people.push(value.wife);
});
$.each(people, function (key, value) {
tx.objectStore(store_name).add(value);
});
};
request = window.indexedDB.open(db_name);
request.oncomplete = function (event) {
if (upgrade) {
doTx(request.result, data);
}
};
request.onsuccess = function (event) {
if (!upgrade) {
doTx(request.result, data);
}
};
request.onupgradeneeded = function (event) {
var db = event.target.result;
db.createObjectStore(store_name, {
keyPath: null,
autoIncrement: true
});
}
}
});
A cursor and console.log shows all entries as being added:

JavaScript/GSON: Access JSON references dynamically over object graph (circular references)

I had the problem, to serialize my Java objects through Google GSON, because of several circular references. All my tries ended up in a StackOverflowException, because GSON is not able to handle those circular references.
As a solution, I found following GraphAdapterBuilder:
http://code.google.com/p/google-gson/source/browse/trunk/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java?r=1170
Example:
https://groups.google.com/forum/#!topic/google-gson/z2Ax5T1kb2M
{
"0x1": {
"name": "Google",
"employees": [
"0x2",
"0x3"
]
},
"0x2": {
"name": "Jesse",
"company": "0x1"
},
"0x3": {
"name": "Joel",
"company": "0x1"
}
}
This is working very well, but I am still not able to access the reference values (0xn) dynamically over the object graph like:
alert(0x3.company.name); --> Should print "Google", but I only receive undefined
Is it somehow possible to achieve this?
Maybe with a custom JSON.parse(ajaxResponse, function(key,value) {} function which replaces the variable with the referenced object tree?
For future users, refer to this answer which uses GraphAdapterBuilder: https://stackoverflow.com/a/10046134/1547266
!! UPDATE, BETTER SOLUTION !!
If you can switch your library, just use FlexJson >>> http://flexjson.sourceforge.net/.
I solved my problem with an own JSON parser:
"ref" is "0x[n]" in the original GraphAdapterBuilder
Source:
$.ajax({
type: "POST",
url: "controller/ajaxmethod.htm",
data: { "var1": var1, "var2":var2},
success: function(response){
var jsonObject = parseGraphGSON(response, 0);
...
},
error: function(e){
alert('Error: ' + e.status);
}
});
function parseGraphGSON(gsonResponse, recursionLevel) {
var maxRecursionDepth = 2;
var jsonObject = JSON.parse(gsonResponse, function(key, value) {
if (typeof value === 'string') {
if (value.indexOf("ref") == 0) {
if (recursionLevel < maxRecursionDepth) {
return parseGraphGSON(gsonResponse, recursionLevel + 1)[value];
} else {
return JSON.parse(gsonResponse)[value];
}
}
}
return value;
});
return jsonObject;
}

Categories

Resources