Given that I have a class defined such as
(function () {
function Dummy(){
var toReturn ={
myProp : "asdf",
myFunc : myFunc
}
return toReturn;
function myFunc(){};
}
})();
how does one get an instance of the same type after
var dummy = new Dummy();
JSON.stringify(dummy);
so that I have myFunc still available on the type.
JSON.parse(JSON.stringify(dummy)) returns same shape of the object by not the same type.
NOTE: I am not asking about capability of JSON, but how do people deal with this in general. Do you hand roll your mapping mechanism so that after parsing from JSON you map it onto instance of the type, or if there is such functionality in some library, such as underscore.
I created a helper function that helps me do this, but would like to hear from others how do you deal with situation like this. As I put in comments, JSON comes over the wire, for which we have a type defined. To get the values from JSON in our type, we parse json, create instance of type and then apply map function below.
function map(fromObj, toObj) {
Object.keys(fromObj)
.forEach(function (key) {
if (typeof fromObj[key] != 'function') {
if (toObj.hasOwnProperty(key)) {
if (typeof fromObj[key] !== 'object') {
toObj[key] = fromObj[key];
} else {
map(fromObj[key], toObj[key]);
}
}
}
}
});
}
Note, Not certain about requirement , if this similar to what posed at Question. If off-topic , please post comment , will withdraw.
Piece was originally composed for this Question Organizing large javascript files [on hold] . With a json response , having "x" type of contents , could map returned object to new object , copying properties utilizing $.extend() .
Result would be new object having both properties and functions of returned data. At piece below, at completion of process , $.Pages begins as function , then type gets converted to object - though it could retain both function and object properties by including || {} at definition stage.
Functions within returned json objects could be called within .then() callback ; see console at jsfiddle , object init functions.
At conclusion , $.Pages object has properties of returned json , including access to functions . Based on a jsonp - type processing flow.
Piece is "frame" of a processing approach ; could extend to include other functionality
$(function() {
var dfd = new $.Deferred();
dfd.progress(function(msg) {
console.log(msg);
});
ProductPage = {
name : "ProductPage",
addToCartBtn: "#add-to-cart",
initName : function() {return dfd.notify(this.name)},
init: function() {
this.initName();
// ProductPage.initAddToCartPopup();
// ProductPage.initSidebar();
}
};
ContactPage = {
name : "ContactPage",
validateEmail : function (e) {return dfd.notify(e)},
initName : function() {return dfd.notify(this.name)},
init: function() {
this.initName();
// ProductPage.initAddToCartPopup();
// ProductPage.initSidebar();
}
};
var mods = function() {
return {"ContactPage" : ContactPage
, "ProductPage" : ProductPage };
};
$.Pages = function() {
$.when(mods())
.done(function(pages) {
$.Pages = pages;
});
return $.Pages
};
$.when($.Pages())
.then(function() {
$.each($.Pages, function(k, v) {
v.init();
})
});
console.log($.Pages)
});
jsfiddle http://jsfiddle.net/guest271314/60kv2439/1/ (see console)
basic approach
$p = {};
var queue = [];
var mods = ["dep1.json", "service1.json"];
var mod = function(m) {
queue.push(m);
if (queue.length === mods.length) {
$.each(queue, function(k, v) {
$p = $.extend(v, $p)
})
}
};
$.each(mods, function(k, v) {
$.getScript(v, function(script, status, jqxhr) {
console.log($p)
})
})
Related
I'm very inexperienced with Backbone, but have inherited another dev's code to maintain. I'm trying to add some new functionality to a model. Here's what I have so far:
var AfeDetailCollection = Backbone.Collection.extend(
{
model: AfeDetailModel,
getSubtotalUSD: function(){
return _.reduce(this.models, function(memo, model, index, list){
return memo + model.getExtCostUSD();
}, 0, this);
},
getSubtotalLocal: function () {
return _.reduce(this.models, function (memo, model, index, list) {
return memo + model.getExtCostLocal();
}, 0, this);
},
hasMixedCurrency: function () {
var currCode = '';
this.models.each(function (model) {
if (currCode == '')
// Identify the currencyCode of the first AfeDetail object in the collection
currCode = model.get('currencyCode');
else if (currCode != model.get('currencyCode'))
// The currencyCode has changed from prior AfeDetail in the collection
return true;
});
return false;
}
}
);
The hasMixedCurrency() function is what I've added. The two pre-existing functions work fine. What I'm trying to do is determine whether the collection of AfeDetail objects contains multiple currencyCode values (a property on the AfeDetail model). So I was simply trying to iterate through this collection until I find a change in the currencyCode and then return true.
In the javascript on my page, however, as soon as I try to check this...
if (this.collection.hasMixedCurrency()) {
...
...I get this: Error: Object doesn't support property or method 'each'
I'm obviously doing something wrong, probably something simple, but I'm just not seeing it.
It is look like this.models is a javascript array, not a backbone collection.
You can try with underscore's each method:
_.each(arr, function(val) {
});
Also your logic in loop looks wrong. Try this code:
hasMixedCurrency: function() {
return _.size(_.uniq(this.models, "currencyCode")) > 1;
}
jsFiddle
Edit: More performanced way
hasMixedCurrency: function () {
return _.find(this.models, function(v) { return v.currencyCode !== _.first(this.models).currencyCode; }) !== undefined;
}
I recently made a custom framework for JavaScript, but my '.css()' function is not working as an object notation, here is a part of my code:
const aps = function(selector) {
if (!(this instanceof aps)) {
return new aps(selector);
};
this.el = document.querySelectorAll(selector);
var about = {
Version: "0.3",
Author: "AppleProSchool, Adam Izgin",
Created: "Fall 2018, Tuesday 5, November",
Updated: "Tuesday 6, November",
}
};
aps.prototype.css = function(property, value) {
this.el.forEach(function(element) {
element.style[property] = value;
});
return this;
};
and for example if I would do this:
(window.onload = function() {
aps('.test').css({ background: '#0f0' });//That does not return anything. Why?
});
But when I do this:
(window.onload = function() {
aps('.test').css('background', '#0f0');//It works.
});
And I do have a div with a background of red.
Any ideas why? Thank you anyways.
Your function expects two arguments:
aps.prototype.css = function(property, value) {
So when you send it one argument (an object):
aps('.test').css({ background: '#0f0' })
the property argument contains {background:'#0f0'} which won't be extracted properly with:
element.style[property]
and the function can't find the information it needs forvalue, so that will be undefined.
But, when you send in two arguments:
aps('.test').css('background', '#0f0')
it works.
If you want to use the Object syntax, you'll need to update your function to expect just a single argument and the function will have to "unpack" the data it needs from that object. It would look like this:
aps.prototype.css = function(obj) {
this.el.forEach(function(element) {
// Use the first key name in the object and the first key value
element.style[Object.keys(obj)[0]] = Object.values(obj)[0];
});
return this;
};
I want to initialize my javascript class like this:
Example.init({"settings": {"baseUrl":"http://example.com"},
"user": {"id":123, "name":"john doe"} );
And then I want to be able to use it like this:
Example.settings.baseUrl
Example.user.id;
Example.user.name;
I am currently using the module pattern like this:
Example = (function(){
var _init = function(data) {
};
return {
init: function($data) {
_init($data);
}
};
})();
Example.module2 = (function(){
var _init = function(data) {
if(Example.user.id > 0) { // referencing data set in the Example.init
}
};
return {
init: function($data) {
_init($data);
}
};
})();
I'm not sure how I can expose these properties, looking for an explanation and guidance.
(please comment on best practise also, should I use $ for parameters and if so when?)
Note: Here is basic outline of what I am trying to do.
The first thing I will do is call the Example.init function in all my pages:
Example.init({"settings": {"baseUrl":"http://example.com"}, "user": {"id":123, "name":"john doe"} );
I want my other modules to be able to reference this data I set in the .init() method, see the Example.module2 I added above.
Is there a timing issue with this?
Example = (function(){
var _init = function(data){
// manually
//this.settings = data.settings || NULL;
//this.user = data.user || NULL ;
for (prop in data){
this[prop] = data[prop];
}
}
return {
init: function(data) {
_init.call(this, data);
}
};
})();
Example.init(
{"settings": {"baseUrl":"http://example.com"}, "user": {"id":123, "name":"john doe"}});
console.log(Example.settings.baseUrl);
console.log(Example.user.id);
console.log(Example.user.name);
As for your other question "using $ in variable names " - It is a comment naming convention when the variable contains a Jquery object -
always in the process of learning Javascript and modifying a cool autocomplete library, i am now in front of this :
i need to check if something passed in an object literal is a variable/field (that is to be considered as a simple value) or is something that can be called.
(as MY autocomplete depend on many input fields, i need to "value" the right things, just before the Ajax.Request) so that this declaration (see the 'extra' parts...)
myAutoComplete = new Autocomplete('query', {
serviceUrl:'autoComplete.rails',
minChars:3,
maxHeight:400,
width:300,
deferRequestBy:100,
// callback function:
onSelect: function(value, data){
alert('You selected: ' + value + ', ' + data);
}
// the lines below are the extra part that i add to the library
// an optional parameter, that will handle others arguments to pass
// if needed, these must be value-ed just before the Ajax Request...
, extraParametersForAjaxRequest : {
myExtraID : function() { return document.getElementById('myExtraID').value; }
}
see the "1 // here i'm lost..." below, and instead of 1 => i would like to check, if extraParametersForAjaxRequest[x] is callable or not, and call it if so, keeping only its value if not. So that, i get the right value of my other inputs... while keeping a really generic approach and clean modification of this library...
{
var ajaxOptions = {
parameters: { query: this.currentValue , },
onComplete: this.processResponse.bind(this),
method: 'get'
};
if (this.options.hasOwnProperty('extraParametersForAjaxRequest'))
{
for (var x in this.options.extraParametersForAjaxRequest)
{
ajaxOptions.parameters[x] = 1 // here i'm lost...
}
}
new Ajax.Request(this.serviceUrl, ajaxOptions );
You can do a typeof to see if the parameter is a function, and call it if it is.
var value;
for (var x in this.options.extraParametersForAjaxRequest)
{
value = this.options.extraParametersForAjaxRequest[x];
if (typeof(value) == 'function') {
ajaxOptions.parameters[x] = value();
}
else {
ajaxOptions.parameters[x] = value;
}
}
if (typeof this.options.extraParametersForAjaxRequest[x]==='function') {
}
You should also do this:
if (this.options.extraParametersForAjaxRequest.hasOwnProperty(x) {
if (typeof this.options.extraParametersForAjaxRequest[x]==='function') {
}
}
when iterating through properties of objects, otherwise you can end up looking at prototype members too.
Another suggestion is to make this more readable with an alias for the thing you're working with. So the ultimate would be:
var opts = this.options.extraParametersForAjaxRequest;
// don't need to check for existence of property explicitly with hasOwnProperty
// just try to access it, and check to see if the result is
// truthy. if extraParametersForAjaxRequest isn't there, no error will
// result and "opts" will just be undefined
if (opts)
{
for (var x in opts) {
if (opts.hasOwnProperty(x) && typeof opts[x]==='function') {
}
}
}
I have this post method
$.post("/abc/export.action",
{
sessiontoken: sessiontoken,
exportType: "xls",
param1:compareData,
param2:comparePatchData},
function(resdata)
{
alert(resdata);
}
);
I wanted some best practice so that I can have enumeration or array which stores all my post parameters in it and use it while post, A way in which I can avoid hardcoding of sessiontoken, param1 etc.. what could be the solution?
EDIT
Sometimes it might happen that I need to change the names of params so i have to edit everywhere I have post method, instead of that if all the params where there in some enum or array it would be much easier to just change at one place.
If you want the labels in a object to be based on some other variable you can do it with
var paramLabels = {"session": "sessionToken", "type": "exportType", ...}
var paramValues = {};
paramValues[paramLabels.session] = sessionToken;
paramValues[paramLabels.type] = "xls"
...
$.post(/"abc/export.action", paramValues, function(resdata) { alert(resdata);});
I can't really see the benefit of this approach expect when the developers of the backend like to change the names of the parameters every five minutes.
Another way of handling this would be to create a factory method or a builder
function createParams(session, type, ...) {
return { "sessionToken": session, "exportType": type, ...) }
}
var params = createParams(sessionToken, "xls", ...);
or
var Parameters = function() {
this.session = function(session) { this.session = session; return this;}
this.type = function(type) { this.type = type; return this;}
...
this.build = function() {
var params = {}
!this.session || params.sessionToken = this.session;
!this.type || params.exportType = this.type;
...
return params;
}
}
var params = new Parameters().session(sessionToken).type("xls")...build();
Both of these approaches let you define the concreate name of the parameters only once. The latter may be easier to reuse when different set of parameters are needed.
Instead of passing an object literal to $.post() you can pass an object that was defined elsewhere in your code, and thus re-use that same object for multiple posts. Or put in a function call that returns an object with the appropriate parameters set up.
var postParams = {
sessiontoken: sessiontoken,
exportType: "xls",
param1:compareData,
param2:comparePatchData
};
$.post("/abc/export.action",
postParams,
function(resdata) { alert(resdata); });
$.post("/abc/import.action",
postParams,
function(resdata) { alert(resdata); });
// or use a function that can return a different object
// depending on some conditions (or parameters passed to
// the function though I haven't shown that)
function getPostParams() {
if (someCondition)
return postParams;
else
return {
sessionToken : something,
// etc.
};
}
$.post("/abc/someother.action",
getPostParams(),
function(resdata) { alert(resdata); });