Angular $resource serialize functions too - javascript

Ik have a JavaScript object with lots of properties and calculated properties (which are functions), which I want to send to the server.
Now when I call this method
resource.save({ id: vm.allData.id }, vm.allData);
(where vm.allData holds the object) only the properties are serialized, and not the results of the functions.
Is it possible to serialize function results also?
So as an example this little object
function Example() {
var obj = this;
this.propa = 5;
this.propb = 10;
this.total = function () {
return obj.propa + obj.propb;
}
}
I want to serialize property propa, property propb and (calculated) property total
Is that possible?

You need to provide some way to evaluate the values to be saved. You can do this outside of the resource.save() or i.e. use transformRequest to do this on the fly. The general concept is to have service to provide dedicated resource like this:
factory("someService", function ($resource) {
return $resource(
'http://your.url.com/api', {}, {
get: {
method: 'POST',
transformRequest: function(data, headers) {
var toBeSaved = {};
for (key in data) {
if (data.hasOwnProperty(key)) {
if(typeof data[key] == function) {
toBeSaved[key] = data[key]();
} else {
toBeSaved[key] = data[key];
}
}
}
return toBeSaved;
}
}
});
});
Beware the assumption made here is that the functions don't require any arguments. Also note that this is just a draft concept so please refer to the $resource documentation for more details.

Related

referencing properties that are yet to be defined

I have an array of objects. The objects reference properties that need to be examined. However, the properties are optional and might not get passed. This results in an error.
Here is a greatly simplified version of the code:
var build = {
execute: function (type, data) {
switch (type) {
case "fullName":
return build.fullName(data);
}
},
fullName: function(data){
return data[0] + data[1]
}
};
// assume this is the actual inbound data after parsing
var sample_event = {
customer: {
firstName: "bob",
lastName: "smith",
email: "a#example.com"
}
};
function cleanStuff( event) {
// potential inbound data
var prospects = [
{parent: "customer", type: build.fullName, newField: 'fullName',data: [event.customer.firstName, event.customer.middleName, event.customer.lastName]},
{parent: "customer", type: build.fullName, newField:'fullName', data: [event.idontexist.firstName, event.seller.middleName, event.seller.lastName]}
];
function calc(type, newField, calculationData) {
var calculated = calculated || {};
return calculated[newField] = build.execute(type, calculationData);
}
var filteredList = prospects.filter(function (x) {
if (event[x.parent] !== undefined) {
return x
}
});
filteredList.forEach(function (item) {
var type = item.type,
fieldName = item.fieldName,
data = item.data;
calc(type, fieldName, data, event);
});
}
cleanStuff(sample_event);
How does one go about referencing properties that don't exist? I DO NOT want to create empty properties, or set them to null. I want to simply avoid scrubbing them if they were not sent in to the app.
I could create the elements as Strings and parse them back to property paths at time of use but I think thats a bad approach.
I was also considering setting up a Proxy using ES6 but Chrome has yet update with the latest spec.
I'm using the 'prospects' array as a lookup table of sorts to say, "hey if this value is sent, do X to it".
I found using the newly introduced Proxy (ES6) works well here and allows me to set traps to handle these scenarios. Very cool addition to JS, allows for a great level of abstraction.
function Event(event) {
var proxy = new Proxy(event, {
get: function (target, property) {
if (property in target) {
return target[property];
} else {
return " ";
}
}
});
return proxy;
}
var event = new Event(sample_event);
Use Babel (via Webpack or Browserify perhaps) to compile ES6 code, and problem solved?
Maybe you can use async functions with Promises that promise the data will eventually be available?

Constructing object to have a constructor method containing a set property method

I'm trying to make an object as a mock to pass into a test.
I don't know if I'm constructing it correctly - I'm getting Error: User() method does not exist.
injectedUser = {
set: function(key, val){
this[key] = val;
}
};
injectedParse = {
Parse: {
User: function() {
return injectedUser;
}
}
};
Parse = function() {
return injectedParse;
};
The desired behavior I'm looking for is for function Parse.User to be called with user = new Parse.User and be able to do user.set("key", "value");
But it seems like I'm not building this object properly? I have another issue but since it is more a javascript thing potentially, I made a new, more general issue about building objects
I also tried
set = function (key, val) {
this[key] = val;
}
mockParse = function(){
this.User()
};
mockParse.prototype.User = function(){
return set
};
$provide.value('Parse', mockParse);
That gave me the same error.
All the information you should know about constructing objects are at: http://ericleads.com/2013/02/fluent-javascript-three-different-kinds-of-prototypal-oo/

Sensible approach to callbacks on object prototype methods in JavaScript/jQuery?

Is what I've done below a sensible approach to allow callbacks to run on functions defined in an object's prototype, such that the scope is correct?
I've been wrestling with the correct way to set the value of this when an object's prototype method is the one to run in response to a callback which might originate from an AJAX request or from a click binding or whatever.
Here is a simplified annotated version:
// everything is inside an object which provides the namespace for the app
var namespace = {
// a fairly vanilla object creation routing, which uses the prototype
// approach for defining the functions on the object
newObj : function(params) {
var MyObj = function(params) {
this.property = params.property
};
MyObj.prototype = namespace.ObjPrototype;
return new MyObj(params);
},
// the prototype itself, which defines 2 related functions
ObjPrototype : {
// The first is called to do some form of asynchronous operation
// In this case it is an ajax call
doAsync: function (params) {
$.ajax({
type: "get",
url: params.url,
data: params.data,
dataType: params.datatype,
success: namespace.objClosure(this, "asyncSuccess", ["data"]),
});
// the final line above is the key here - it asks a function (below)
// for a closure around "this", which will in turn run the
// function "asyncSuccess" (defined next) with the argument "data"
},
// This is the actual callback that I want to run. But we can't
// pass this.asyncSuccess to the ajax function above, because the
// scope at execution time is all wrong
asyncSuccess : function(params) {
this.property = params.data;
},
},
// This is the bit I sort of invented, to help me around this problem.
// It returns a function which provides a closure around the object
// and when that returned function is run it inspects the requested
// arguments, and maps them to the values in the JS default
// "arguments" variable to build a parameters object which is then
// passed to the function on the object
objClosure : function(obj, fn, args) {
return function() {
if (args) {
var params = {};
for (var i = 0; i < args.length; i++) {
params[args[i]] = arguments[i];
}
obj[fn](params);
} else {
obj[fn]();
}
}
}
}
Now, obviously the actual target callback MyObj.asyncSuccess needs to know that it's going to get a params object, and what structure it will be, and that knowledge has to be shared by the invoking function MyObj.doAsync, but otherwise this seems to work well.
My question is - am I totally mad? Have I missed something obvious that would solve this problem for me in a simpler/less convoluted way? Am I just too far down the rabbit hole by this stage?
I've read around a lot of questions on SO and they have all addressed part of my question, but I don't seem to have got to the bottom of a generally accepted solution for this. I can't be the only person who's ever wanted to do this :)
Edit
I've accepted the answer below, but you need to read all the comments too for it to come together. Thanks folks for your help!
aren't you over complicating things? see if the below code will help you. i did not completely understand your intent but the below code should help you
function newObj(params) {
function asyncSuccess(params) {
this.property = params.data;
}
function objClosure(obj, fn, args) {
return function() {
if (args) {
var params = {};
for (var i = 0; i < args.length; i++) {
params[args[i]] = arguments[i];
}
obj[fn](params);
} else {
obj[fn]();
}
}
}
this.property = params.property
this.doAsync = function (params) {
console.log('reached async');
$.ajax({
type: "get",
url: params.url,
data: params.data,
dataType: params.datatype,
success: objClosure(this, "asyncSuccess", ["data"]),
});
}
}
var k = new newObj({'property':'xyz'});
k.doAsync();
After seeing the comment from "GameAlchemist" i looked into objClosure function i think we can further improvise by using below code: I am still not sure what the value of this.property or data is to give a proper solution hence just assuming few things
function newObj(params) {
function asyncSuccess(params) {
this.property = params ? params.data : null;
}
function objClosure(args) {
return function() {
if (args) {
var params = {};
for (var i = 0; i < args.length; i++) {
params[args[i]] = arguments[i];
}
asyncSuccess(params);
} else {
asyncSuccess();
}
}
}
this.property = params.property
this.doAsync = function (params) {
console.log('reached async');
$.ajax({
type: "get",
url: params.url,
data: params.data,
dataType: params.datatype,
success: objClosure(["data"]),
});
}
}
Few issues here:
if you are already passing params.data to data i.e. data:params.data how can you again assign the value this.property = params.data? Few things are confusing but i hope the above solution works : )

Configuring a service that depends on another service angularjs

What I need to know is how can I use a service like $http outside of the $get function or is it even possible? My code goes and loads a json file that provides a dictionary that my application makes use of in various ways. I want users to be able to customize this dictionary so I'm using jquery's extend method to allow users to add values to an object and extend the dictionary. The instantiate method in the code below handles all of this. What I'd like to be able to do is configure my service like so
config(['_sys_dictionaryProvider', function(_sys_dictionaryProvider) {
_sys_dictionaryProvider.instansiate('config/dictionary/custom/dictionary.json');
}])
But this requires the $http service to be available at the time of configuration and I don't think it is. If I put the $http service as part of the $get property it will work, as explained here, except then the network has to be queried every time the service is used. Is there any way to use a service in the configuration of another service?
Full code below, let me know if I need to clarify.
app.provider("_sys_dictionary", ['$http',
function ($http) {
var dictionary,
DictionaryService = function () {
this.definitions = dictionary;
this.define = function (what) {
var definitions = this.definitions;
if (what instanceof Array) {
for (var i = 0; i < what.length; i++) {
definitions = definitions[what[i]];
}
return definitions;
}
return this.definitions[what];
};
};
return {
$get: function () {
return new DictionaryService();
},
instansiate: function (path) {
$http.get('config/dictionary/dictionary.json').success(function (data) {
dictionary = data;
$http.get(path).success(function (data) {
jQuery.extend(true, dictionary, data)
});
});
}
};
}
]);
Seeing as I don't believe it is possible to use a service in the configuration stage, since there is no way to guarantee that the service your using itself has been configured, I went this route instead
app.provider("_sys_dictionary", function () {
var dictionary,
DictionaryService = function () {
this.definitions = dictionary;
this.define = function (what) {
var definitions = this.definitions;
if (what instanceof Array) {
for (var i = 0; i < what.length; i++) {
definitions = definitions[what[i]];
}
return definitions;
}
return this.definitions[what];
};
};
return {
$get: [
function () {
console.log(dictionary);
return new DictionaryService();
}
],
instansiate: function (path) {
jQuery.ajax({
url: 'config/dictionary/dictionary.json',
success: function (data) {
dictionary = data;
jQuery.ajax({
url: path,
success: function (data) {
jQuery.extend(true, dictionary, data);
},
async: false
});
},
async: false
});
}
};
});
I ended up using jquery's ajax object and turned async to false since I need the dictionary to be ready before the service gets used. Hope this helps someone. If anyone knows a better way of doing this I'd love to know.

Why has the author of this code used the .call method? Surely he could have just accessed the prototype?

Im looking through some code (unfortunatly the author isnt around anymore) and im wondering why he has used the .call method.
hmlPlaylist.prototype.loadVideos = function () {
var scope = this;
this.config.scriptUrl = '_HMLPlaylistAjax.aspx?' + Math.random();
jQuery.ajax({
type: 'GET',
url: this.config.scriptUrl,
success: function (d, t, x) {
scope.loadVideos_callback.call(scope, d);
},
error: function () {
}
});
};
hmlPlaylist.prototype.loadVideos_callback = function (data) {
var jsonData = '';
var jsonError = false;
try {
jsonData = eval("(" + data + ")");
} catch (jError) {
jsonError = true;
}
if (!jsonError) {
if (jsonData.playlists.length > 0) {
this.buildPlaylistList(jsonData.playlists);
}
if (jsonData.videos.length > 0) {
this.buildVideoList(jsonData.videos);
this.bindVideoNavs();
}
}
else {
// no json returned, don't do anything
}
};
Obviously he seems to have used it to pass a 'this' reference to the loadVideos_callback method but why? The 'loadVideos_callback' method is attached to the prototype of 'hmlplaylist' which is the 'class'. So if you access this inside the 'loadVideos_callback' method you get to the same thing dont you?
yes, I think you are right (I can't see the code in action). You still need the closure around scope, but in this case the use of call is not necessary.
To pull some of the comments into this answer, this is always the context on which the method was invoked. So if a new instance of htmlPlayList was created, and the method invoked on that instance, this would be a reference to that instance.

Categories