In my webapp, I have a model with properties whose names are dynamically generated based on data from the server. For example, I would normally reference this by doing something like this from my controller:
var str1 = 'property.name.with.dots'; // String from server
this.get('model.someProperty')[str1].integer = 2;
this.get('model.someProperty')[str1].integer += 1;
But Ember doesn't like this - it says I should use a set or get function. Which makes sense. So I want to do something like this in place of the last line above:
this.get('model.someProperty.' + str1).incrementProperty('integer');
This would work fine out of the box if str1 didn't have dots. It does, though, so what can I do to get Ember's getters to work? I tried
this.get('model.someProperty')[str1].incrementProperty('integer');
but it doesn't work - the subobjects don't get Ember's methods by default.
Definitely
Massage the data before handing it off to Ember, having dots in your name will just cause a plethora of chaining problems.
Clean the data, I chose _ (this isn't deep cleaning, exercise for your fun)
App.cleanData = function(result){
var response = {},
re = new RegExp('\\.', 'g'),
newKey;
for(var key in result){
newKey = key.replace(re, '_');
response[newKey] = result[key];
}
return response;
};
Use the cleaned data instead of the server data
App.FooRoute = Em.Route.extend({
model: function(){
return $.getJSON('/foo').then(function(result){
return App.cleanData(result);
}
}
});
Related
So I've been working on this project but I'm stuck because I can't figure out how I should go about setting the other values of this new JSON object. So basically on the front end I have this:
HTML page view. The 'cat4' ID is the new object I tried to create, and illustrates the error I'm trying to fix. The problem is that I'm having trouble setting the LIMIT value of newly created objects (or multiple values at all). Here is the code where the object is created:
function sendCat()
{
window.clearTimeout(timeoutID);
var newCat = document.getElementById("newCat").value
var lim = document.getElementById("limit").value
var data;
data = "cat=" + newCat + ", limit=" + lim;
var jData = JSON.stringify(data);
makeRec("POST", "/cats", 201, poller, data);
document.getElementById("newCat").value = "Name";
document.getElementById("limit").value = "0";
}
In particular I've been playing around with the line data = "cat=" + newCat + ", limit=" + lim; but no combination of things I try has worked so far. Is there a way I can modify this line so that when the data is sent it will work? I find it odd that the line of code works but only for setting one part of the object.
The JSON.stringify() method converts a JavaScript object or value to a JSON string, optionally replacing values if a replacer function is specified or optionally including only the specified properties if a replacer array is specified.
MDN
I think this is what you want:
const newCat = 'Meow';
const newLimit = 5;
const data = {
cat: newCat,
limit: newLimit
}
console.log(JSON.stringify(data));
What you're referring to as a 'JSON object' is actually just a javascript object, you can make one using object literal syntax. An object literal with multiple properties looks like this:
var data = {
cat: newCat,
limit: lim
};
makeRec("POST", "/cats", 201, poller, JSON.stringify(data));
assuming the fifth parameter to makeRec is supposed to be the POST request body as stringified JSON, as your code seems to imply
I need to serialize and deserialize JavaScript objects to store them in a DB.
Note that these objects contain functions, so I can't store them as JSON, so I can't use json2.js.
What's the state of the art in [de]serialization of JavaScript objects (in JavaScript of course).
In general, there's no way (in a browser) to serialize objects with functions attached to them: Every function has a reference to its outer scope, that scope won't exist when you deserialize it, so serialized references to that scope will be invalid.
What I would do is use the built-in (or json2.js) JSON.stringify and JSON.parse functions with the replacer and reviver parameters. Here's a partial example of how it would work:
JSON.stringify(yourObject, function(name, value) {
if (value instanceof LatLng) { // Could also check the name if you want
return 'LatLng(' + value.lat() + ',' + value.lng() + ')';
}
else if (...) {
// Some other type that needs custom serialization
}
else {
return value;
}
});
JSON.parse(jsonString, function(name, value) {
if (/^LatLng\(/.test(value)) { // Checking the name would be safer
var match = /LatLng\(([^,]+),([^,]+)\)/.exec(value);
return new LatLng(match[1], match[2]);
}
else if (...) {
...
}
else {
return value;
}
});
You can use any serialization format you want in your custom types. The "LatLng(latitude,longitude)" format is just one way of doing it. You could even return a JavaScript object that can be serialized to JSON natively.
You don't want to serialize logic such as functions.
If you have to update your logic / js functions in the future, you don't (always) want the older logic to be loaded back with the data neccessarily. Beware.
use gserializer:
http://www.onegeek.com.au/articles/programming/javascript-serialization.php
the code in google :
http://code.google.com/p/gserializer/
GSerializer is a javascript library to
serialize/deserialize javascript
objects to and from strings, for
persistance in say, a Cookie. Unlike
many other implementations,
GSerializer can also serialize
functions and non-JSON notation.
On Node.js, there is also the JASON package.
Here is the example:
var JASON = require("JASON");
str = JASON.stringify(obj);
obj = JASON.parse(str);
Install the package by: npm install JASON.
If you're using ES6 versions of Node, you can check out a small package I wrote called JSOFF. It's the JavaScript Object-Function Format; a drop-in replacement for JSON that handles functions.
It's super tiny and simple, so Babeljs or Browserify may be your friends.
Install via: npm install jsoff or yarn add jsoff.
Here is the example how to create an object with functions:
const JSOFF = require('jsoff');
var obj = {
abc: 123,
def: function (a,b) { return a * 2 + b * 3; },
ghi: a => { return a * 2 },
jkl: (a,b) => { return ((d,e) => { return a*d + b*e })(2,4) }
};
var str = JSOFF.stringify(obj);
// str is now:
// '{"abc":123,"def":"function (a,b) { return a * 2 + b * 3; }","ghi":"a => { return a * 2 }","jkl":"(a,b) => { return ((d,e) => { return a*d + b*e })(2,4) }"}');
});
var clone = JSOFF.parse(str);
clone.def(10,5) // 35
clone.ghi(5) // 10
clone.jkl(10,20) // 100
I wouldn't serialize JS functions because of security reasons. Through a public API all kinds of nasty things could be sent to the database. As for deserialisation I've got a different approach. I'm mixing model objects defined on client side with the data coming from JSON. I have a small tool to do that, take a look at it on GitHub at khayll/jsonmix.
JsonMix provides a kind of deserialisation from JSON into JavaScript Objects complete with functions.
It would look like something:
//model definition (just an example)
var LatLng = function() {}
LatLng.prototype.getMapTypeId = function() {
return this.mapTypeId;
}
//deserializing done like this
var result = JSMix(jsonString).withObject(LatLng.prototype, "latLngs").build();
//all items in the latLngs collection have the functions coming from the model
console.log(result.latLngs[5].getMapTypeId());
How am I able to create an index for the object data that I am passing into Firebase?
I am using the .$add function in the AngularFire library to push the data. This is the filter and controller that I am using:
angular.module('bestDay', ["firebase"]).factory("GreatService", ["$firebase", function($firebase) {
var ref = new Firebase("https://quickjournal.firebaseIO.com/");
return $firebase(ref);
}])
.controller("bdctrl", ["$scope", "GreatService",
function($scope, greatService) {
$scope.theval = "Val " + Math.round(Math.random()*101);
$scope.daylist = greatService;
$scope.addDayGood = function() {
$scope.daylist.$add({
desc: $scope.newDay.desc,
date: $scope.newDay.date,
value: $scope.theval
});
$scope.newDay.desc = "";
$scope.newDay.date = "";
};
}
]);
As you can see, I was attempting to use a unique value when passing the objects in, but it was only generating the same number every time (13). If it isn't apparent, I am semi-new to programming.
I would also like to be able to write a function that will remove the data by that index. Since I am unable to conquer the prior task, I may need assistance in doing this as well.
I am writing my code with the angularjs library.
I have combed through the firebase and angularfire library documentation with no results. If you could point me to a URL with the documentation on this, it would be much appreciated.
Firebase should do the indexing, as this makes it easier if you have more than one user accessing the same data.
Relevant to your question, you should look up https://www.firebase.com/docs/ordered-data.html for working with lists in firebase.
More the point, the push() function provided makes for easy chronological sorting, and if you need more complex sorting you can look at the setWithPriority() function.
angular.module('bestDay', ["firebase"])
.controller("bdctrl", ['$scope', '$firebase',
function($scope,$firebase) {
var daysRef = new Firebase("https://quickjournal.firebaseIO.com/daylist/");
$scope.dayList = $firebase(daysRef);
$scope.dayLocationInFirebase = daysRef.push();
$scope.addDayGood = function(){
// Setdata to the generated location
$scope.dayLocationInFirebase.set({
desc: $scope.newDay.desc,
date: $scope.newDay.date
});
//holds reference to location the object was pushed to, for direct manipulation of the value. Pass it to the scope or an array if you need it for later
var pushedName = $scope.dayLocationInFirebase.name();
alert(pushedName);
$scope.newDay.desc = "";
$scope.newDay.date = "";
}
}
]);
I've seen very similar questions to this, but I can't quite decide if they was answered clearly - maybe I'm being a bit dense, sorry.
I want to have the convenience (and clarity) of my own object, call it a CardboardBox(). It won't contain code, just data. I want to write this to a database and read it back later, but obviously, it is a type Object() when it's read back. All I can think of to find out what it used to be is:
Have a member variable type that I set to CARDBOARD_BOX
Instantiate a new CarbardBox() and use a function (in the box) to copy the properties of Object() to the new CardboardBox() object
Is there a better way of doing this? I'm pretty sure I can change the actual type.
function CardboardBox() {
this.type = "CARDBOARD_BOX"
this.name = "No set";
this.populate = new function(obj) {
// populate this object with obj properties
}
var box = new CarboardBox(); // CarboardBox
box.name = "My Box";
send = JSON.stringyfy(box);
.
.
.
obj = JSON.parse(send); // Object
if (obj.type == "CARDBOARD_BOX") {
savedBox = new CardboardBox();
savedBox.populate(obj);
}
Thanks in advance...
Steve
[edit] My test code.
function CardboardBox(n) {
this.name = n;
}
var box = new CardboardBox("My Box");
send = JSON.stringify(box); // JSON CarboardBox()
obj = JSON.parse(send, function fn(obj) { // Object() returned
log("OB: "+obj.type);
return obj.type === 'CardboardBox' ? new CardboardBox(obj) : CardboardBox;
});
console.log(obj);
Output is:
OB: undefined utils.js:40
OB: undefined utils.js:40
function CardboardBox(n) {
this.name = n;
}
One possible solution is the following:
function CardboardBox(n) {
if(typeof(n) == 'string') {
//build from name string
this.name = n;
} else {
//build from object
this.name = n.name;
}
//add in this object's "type" in a place
//that is unlikely to exist in other JSON strings
this.__type = 'CardboardBox';
}
var box = new CardboardBox("My Box");
send = JSON.stringify(box), // JSON CarboardBox()
obj = JSON.parse(send, function(key, val) {
//if this is an object, and is CardboardBox
if(typeof(val) === 'object' && val.__type === 'CardboardBox')
return new CardboardBox(val);
return val;
//or if your object is in a context (like window), and there are many of
//them that could be in there, you can do:
//
//if(typeof(val) === 'object' && context[val.__type])
// return new context[val.__type](val);
});
console.log(obj);
Basically store the object type in a place you know to look for later on when parsing the json. if you have multiple objects you can instantiate in a single scope the second parse method may be more appropriate. This also will account for objects in the JSON that are not CarboardBoxs.
Edit Here is a jsFiddle of this method in action.
Overall, you're correct: Javascript doesn't have any built-in way to serialize anything beyond plain objects, so going to and from JSON will not produce a particular class when you deserialize it. So you need to either work out serialization/deserialization yourself, or use a library that provides some support.
I personally like Backbone.js for this problem, as it handles serializing and deserializing quite well. You define a model class, which include a method to save its data to a server in a serialized form, and a method to deserialize it back to the model. The key design issue here is that deserializing is performed knowing the model you're deserializing to:
you either call myModel.fetch() to get data from the server based on the model id, or
you pass a bunch of new data to the model constructor: new Model(serializedData), or
you pass an array of data for multiple models to a collection that knows the model type: new ModelCollection(arrayOfSerializedData).
What Backbone doesn't do is deal with type-casting data of an unknown type. When I've dealt with this, I've usually done something similar to #Chad's response, but using an intermediary; you could see this as a proxy model, or as a factory:
var classes = {
CardboardBox: ...,
AluminumBox: ...
}
function Deserializer(json) {
// parse if you're actually dealing with a string
var data = JSON.parse(json),
// now look for some custom type flag - you'll need to set this yourself
type = data.type,
// class lookup, perhaps with a default
Cls = classes[type] || DefaultType;
return new Cls(data);
}
var obj = new Deserializer(send);
obj instanceof CardboardBox; // should work
This still relies on a custom flag to switch types, though - I'm not sure there's any way around this.
I need to serialize and deserialize JavaScript objects to store them in a DB.
Note that these objects contain functions, so I can't store them as JSON, so I can't use json2.js.
What's the state of the art in [de]serialization of JavaScript objects (in JavaScript of course).
In general, there's no way (in a browser) to serialize objects with functions attached to them: Every function has a reference to its outer scope, that scope won't exist when you deserialize it, so serialized references to that scope will be invalid.
What I would do is use the built-in (or json2.js) JSON.stringify and JSON.parse functions with the replacer and reviver parameters. Here's a partial example of how it would work:
JSON.stringify(yourObject, function(name, value) {
if (value instanceof LatLng) { // Could also check the name if you want
return 'LatLng(' + value.lat() + ',' + value.lng() + ')';
}
else if (...) {
// Some other type that needs custom serialization
}
else {
return value;
}
});
JSON.parse(jsonString, function(name, value) {
if (/^LatLng\(/.test(value)) { // Checking the name would be safer
var match = /LatLng\(([^,]+),([^,]+)\)/.exec(value);
return new LatLng(match[1], match[2]);
}
else if (...) {
...
}
else {
return value;
}
});
You can use any serialization format you want in your custom types. The "LatLng(latitude,longitude)" format is just one way of doing it. You could even return a JavaScript object that can be serialized to JSON natively.
You don't want to serialize logic such as functions.
If you have to update your logic / js functions in the future, you don't (always) want the older logic to be loaded back with the data neccessarily. Beware.
use gserializer:
http://www.onegeek.com.au/articles/programming/javascript-serialization.php
the code in google :
http://code.google.com/p/gserializer/
GSerializer is a javascript library to
serialize/deserialize javascript
objects to and from strings, for
persistance in say, a Cookie. Unlike
many other implementations,
GSerializer can also serialize
functions and non-JSON notation.
On Node.js, there is also the JASON package.
Here is the example:
var JASON = require("JASON");
str = JASON.stringify(obj);
obj = JASON.parse(str);
Install the package by: npm install JASON.
If you're using ES6 versions of Node, you can check out a small package I wrote called JSOFF. It's the JavaScript Object-Function Format; a drop-in replacement for JSON that handles functions.
It's super tiny and simple, so Babeljs or Browserify may be your friends.
Install via: npm install jsoff or yarn add jsoff.
Here is the example how to create an object with functions:
const JSOFF = require('jsoff');
var obj = {
abc: 123,
def: function (a,b) { return a * 2 + b * 3; },
ghi: a => { return a * 2 },
jkl: (a,b) => { return ((d,e) => { return a*d + b*e })(2,4) }
};
var str = JSOFF.stringify(obj);
// str is now:
// '{"abc":123,"def":"function (a,b) { return a * 2 + b * 3; }","ghi":"a => { return a * 2 }","jkl":"(a,b) => { return ((d,e) => { return a*d + b*e })(2,4) }"}');
});
var clone = JSOFF.parse(str);
clone.def(10,5) // 35
clone.ghi(5) // 10
clone.jkl(10,20) // 100
I wouldn't serialize JS functions because of security reasons. Through a public API all kinds of nasty things could be sent to the database. As for deserialisation I've got a different approach. I'm mixing model objects defined on client side with the data coming from JSON. I have a small tool to do that, take a look at it on GitHub at khayll/jsonmix.
JsonMix provides a kind of deserialisation from JSON into JavaScript Objects complete with functions.
It would look like something:
//model definition (just an example)
var LatLng = function() {}
LatLng.prototype.getMapTypeId = function() {
return this.mapTypeId;
}
//deserializing done like this
var result = JSMix(jsonString).withObject(LatLng.prototype, "latLngs").build();
//all items in the latLngs collection have the functions coming from the model
console.log(result.latLngs[5].getMapTypeId());