IndexedDB and Javascript: JSON and objects misunderstanding - javascript

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:

Related

Javascript push array inside object

How do I create the data array from my second api call result into the format I want?
I have a code like this
var github = require('octonode');
var client = github.client();
var userName = "octocat";
var repoName = "";
var branchName = "";
var data = [];
var branches = [];
client.get('/users/'+userName+'/repos', {}, function (err, status, body, headers) {
body.forEach(function(obj) {
repoName = obj.name;
//==============================
client.get('repos/'+userName+'/'+repoName+'/branches', {}, function (errx, statusx, bodyChild, headersx) {
bodyChild.forEach(function(objChild) {
branchName = objChild.name;
});
});
});
});
I have received repoName and branchName data as well.
I want my data format like
How to use
data.push({
name: repoName,
branches: 'branchName loooping here for every repoName'
});
so branches repetition data can be contained in my branches tag
Thank you
I guess you can do something like this:
var data = [];
client.get('/users/'+userName+'/repos', {}, function (err, status, body, headers) {
body.forEach(function(obj) {
repoName = obj.name;
client.get('repos/'+userName+'/'+repoName+'/branches', {}, function (errx, statusx, bodyChild, headersx) {
let elem = {"name": repoName, "branches": []}; //create json object for each repo
bodyChild.forEach(function(objChild) {
elem.branches.push(objChild.name); //push all branchs to that elem
});
data.push(elem); // add the elem to the data array
});
});
});
So in this case data is an object, that has a property name which is string, and another property branches which is array. If you want to push data to the property branches you can just call the push() function on it.
Please check the example below:
let data = {
name: "repoName",
branches: [
{
name: "foo"
}
]
}
data.branches.push(
{
name: "bar"
}
);
console.log(data);

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

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

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

Return object from multiple getJSON responses

I have a function that calls a paginated API, loops through each of the pages and returns data from from each of the responses:
function nextPage(url) {
if (!url) {
return;
}
$.getJSON(url, function(data) {
nextPage(data.next);
$.each(data.results, function (k, v) {
console.log(v.city_name);
});
});
}
nextPage('http://url.com/api/v1/cities/?page=1');
Currently, when I log the response to the console I get something that looks like this:
Paris
Brussels
Mexico City
...
But what I want is a structure that looks this this:
[
{item: 'Paris'},
{item: 'Brussels'},
{item: 'Mexico City'}
]
For reference the response data looks like this:
{
"count": 105,
"next": "http://url.com/api/v1/cities/?page=2",
"previous": null,
"results": [
{
"city_detail": "http://url.com/api/v1/cities/1/",
"city_name": "Paris"
},
{
"city_detail": "http://url.com/api/v1/cities/2/",
"city_name": "Brussels"
},
{
"city_detail": "http://url.com/api/v1/cities/3/",
"city_name": "Mexico City"
}
]
}
I know the answer is staring me in the face and is probably v. simple, but my lack of javascript knowledge is letting me down. Would appreciate help solving this. Thanks!
EDIT
The problem seems to be that the nextPage function makes multiple calls, returning data from each page of JSON. I can get this data into multiple objects, but not one object representing all the responses.
So basically you want to store key:value pair to an object..:
Dot Notation:
var cities = {}
function nextPage(url) {
if (!url) {
return;
}
$.getJSON(url, function(data) {
nextPage(data.next);
$.each(data.results, function (k, v) {
console.log(v.city_name);
cities.name = v.city_name //Push to an object
});
});
}
So youll get this as a response:
cities = [
{city: 'Paris'},
{city: 'Brussels'},
{city: 'Mexico City'}
]
Bracket Notation:
var cities = {}
function nextPage(url) {
if (!url) {
return;
}
$.getJSON(url, function(data) {
nextPage(data.next);
$.each(data.results, function (k, v) {
console.log(v.city_name);
cities["name"]= v.city_name //Push to an object
});
});
}
Result
:
(Same for both)
cities = [
{city: 'Paris'},
{city: 'Brussels'},
{city: 'Mexico City'}
]
EDIT
Ok so there was another problem! It was sneaky but we finnaly caught it. So the problem was here:
$.getJSON(url, function(data) {
console.log(data.results)
nextPage(data.next);
$.each(data, function (k, v) {
cities["title"]= v.title //Push to an object
});
});
There now, you see it says data.results ... Actually data.results is an empty undefined variable.. What you should do is only data which consists the whole data. You might not understand it now but look over the code i represented below ...
var dataJSON;
function nextPage(url) {
if (!url) {
return;
}
$.getJSON(url, function(data) {
console.log(data) //This consits the data on the below provided url...
dataJSON = data; // Just making a public variable assigned to the data..
nextPage(data.next);
$.each(data, function (k, v) {
});
});
}
nextPage('https://cdn.rawgit.com/amanuel2/Collabs/master/list.json')
Plunker go to console to view data....
EDIT2
Ok this is preety much your JSON File i put in GITHUB, so i have a secure HTTPS://.. Anways here i just did data.results and i got this result:
And here is the Plunker! Hope this helped!!
EDIT3
Ok now this is the last bit.. With your 2nd JSON Page.. You 2nd JSON Page had actually syntax errors.. So i fixed those put it in github and uploaded the new JSON.. Here is the plunker..
Link to Read More about Objects..
var results = []
function nextPage(url) {
if (!url) {
return;
}
$.getJSON(url, function(data) {
nextPage(data.next);
$.each(data.results, function (k, v) {
results.push({item: v.city_name})
console.log(v.city_name);
});
});
}
console.log(results)
// should show an array with {item: city_name} objects.
this allows the results array to be accessed in the outer scoped as well.

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