This might be a stupid problem, but I have an object in which I wanted to store references of other functions/variables and later use them.
For example,
var obj = {
keyA: {
ref: window.objects.someAAA
},
keyB: {
ref: window.objects.someBBB
}
}
Here, when I set this object, window.objects is empty, so accessing those keys (someAAA and someBBB) will give undefined.
I know I was dumb when I first thought of assigning them like this and expecting their actual values after they're loaded, to be available via obj.keyA and obj.keyB. But obviously, they still contain undefined.
But is there a way to do that! I know if I set string instead of reference, I can eval, but any better solution?
Think this should work
var obj = {
keyA: {
ref: function() { return window.objects.someAAA; }
},
keyB: {
ref: function() { return window.objects.someBBB; }
}
}
You can then access via
obj.keyA.ref()
Test
Add the code above then do
window['objects'] = {};
window['objects']['someAAA'] = 'Hallo';
obj.keyA.ref() // will Output 'Hallo'
Try using Getters for such case:
var obj = {
get keyA(){
return (window['objects'])? window.objects.someAAA : null
},
get keyB() {
return (window['objects'])? window.objects.someBBB : null
}
}
// as now window.objects doesn't exists
console.log(obj.keyA); // outputs "null"
window.objects = {'someAAA': "aaa-value", 'someBBB': "bbb-value"};
console.log(obj.keyA, obj.keyB); // outputs "aaa-value" "bbb-value"
Related
In my code I have to analyse JSON objects. I use a small function set:
visit = function(object) {
if (isIterable(object)) {
forEachIn(object, function (accessor, child) {
visit(child);
});
}
else {
var value = object;
console.log(value);
}
};
forEachIn = function(iterable, functionRef) {
for (var accessor in iterable) {
functionRef(accessor, iterable[accessor]);
}
};
isIterable = function(element) {
return isArray(element) || isObject(element);
};
isArray = function(element) {
return element.constructor == Array;
};
isObject = function(element) {
return element.constructor == Object;
};
If I throw now a JSON Object to the visit function, it give me just the value to the console. But I expected the key/value combination. Example:
Code throw
aa03ddbffe59448fb8a56f6b80e650053
But I expect
uuid: aa03ddbffe59448fb8a56f6b80e650053
Is there anything I misunderstand?
I think the value variable must contain a different type from what you're expecting. You could try putting a breakpoint on that line of code and inspecting the object to check what it is. You're expecting the value variable to contain an object with a single uuid property, but it looks to me like the variable actually just contains a string.
So I am pulling in an object that I want to "edit", with a bit of help I have a function that finds the item i'm looking for and replaced the value. What I did no account for when building this was if the items don't exist yet.
So right now the function looks like this :
myFunction = function(moduleName, stateName, startVar){
//currentState brought in from factory controlling it
var currentState = StateFactory.current();
_.each(currentState, function(item) {
if (item.module === moduleName) {
_.each(item.customUrl, function(innerItem) {
if (_.has(innerItem, stateName)) {
innerItem[stateName] = startVar;
}
});
}
});
}
So - this does a great job of replacing the startVar value, assuming it already exists. I need to add some levels of checks to make sure the items exist (and if they don't add them in).
So, for reference, this is what the currentState looks like
[{
"module": "module1",
"customUrl": [
{ "mod1": "2" },
{ "mod2": "1" }
]
}, {
"module": "module2",
"customUrl": [
{ "mod3": "false" },
{ "mod4": "5" }
]
}
];
And so if i passed
myFunction("module1","mod1",3);
This works great, however if I pass
myFunction("module5","mod8","false");
Or maybe something in between like
myFunction("module1","mod30","false");
This function will not handle that scenario. I could use some helpe wrapping my head around how to tackle this problem. Also, am using underscore (if it is required to help). Thank you for taking the time to read!
As mentioned by Phari - something to the effect of this
currentState[moduleName].customUrl[stateName] = startVar;
I was thinking I could just create the object and just _.extend, but because it is an array of objects that wont quite work.
Here's what I mean :
var tempOb = {"module" : moduleName, "customUrl" : [{stateName : startVar}]};
_.extend(currentState, tempOb);
Doesn't quite work right with an array of objects.
It seems that all you need to do is remove the if statement:
if (_.has(innerItem, stateName)) {
innerItem[stateName] = startVar;
}
should become simply:
innerItem[stateName] = startVar;
Then if the property is not present, it will be added. If it is already present, it will be overwritten.
EDIT: to handle absence at top level:
myFunction = function(moduleName, stateName, startVar){
//currentState brought in from factory controlling it
var currentState = StateFactory.current();
var found = false;
_.each(currentState, function(item) {
if (item.module === moduleName) {
found = true;
_.each(item.customUrl, function(innerItem) {
if (_.has(innerItem, stateName)) {
innerItem[stateName] = startVar;
}
});
}
});
if ( ! found ) {
var newItem = {
module: moduleName,
customUrl: []
};
var newInnerItem = {};
newInnerItem[stateName] = startVar;
newItem.customUrl.push(newInnerItem);
currentState.push(newItem);
}
}
I'd like to set a property to an object using prototype, if it's possible, so that when I try to get an undefined value, it returns a default instead of undefined, like:
obj = {
lang : {en: 'hello', it:'ciao'}
}
Example 1: obj.lang.es (undefined) - I'd like to get obj.lang.en instead of undefined.
Example 2: obj.something.else - I'd like to get false instead of an error because it can't read else of undefined.
It is probably not a good idea.
Better use a root or default key:
obj = {
lang : {root: 'hello', en: 'hello', it:'ciao'}
}
So you can ask for a lang in this way:
var hi = obj.lang.es || obj.lang.root;
You can use this in combination with a getter method:
var getTranslation = function(lang) {
return obj.lang[lang] || obj.lang.root;
};
var hi = getTranslation("es");
This is not a pretty solution, but here it goes, create a prototype object that for any language defers the result to a default language. Then your particular object inherits from that prototype object and overrides any value it wants.
var languages = ['en', 'it', 'es']; // fill with every language you will ever use
var defaultLang = 'en';
var protoLang = Object.create(null);
function getDefaultLanguage(){
return this[defaultLang];
}
languages.forEach(function(language){
Object.defineProperty(protoLang, language, {
get : getDefaultLanguage
});
});
var obj = {
lang : Object.create(protoLang, {
en : { value : 'hello' },
it : { value : 'ciao' }
})
};
obj.lang.es // "hello"
obj.lang.en // "hello"
obj.lang.it // "ciao"
The thing is that you have to define every property first, that is why you need the languages array.
I agree with #DCoder. Maybe something like this:
function getLang(lang) {
var result = this.obj[lang];
if ( typeof result == 'undefined' ) {
result = this.obj.en;
}
return result;
}
You do not need ; inside object, there no content in obj.lang, instead try it like this:
obj = {
lang: {en: 'hello', it: 'ciao'}
};
if (obj.lang.hasOwnProperty("en")) {
console.log(obj.lang.en);
} else {
console.log(false);
}
I am using a complex object graph serialized to JSON with MVC4/jQuery/Sammy/Rivets for SPA functionality.
I have a object graph that looks a bit like this when serialized to JSON (obviously mocked-up):
model =
{
Name: "Me",
Age: 22,
Hobbies:
[
{ Name: "Biking", IsActive: true },
{ Name: "Programming", IsActive: true }
]
}
Everything works quite well until I need Unobtrusive validation, since my Hobbies are in a SlickGrid and I am managing all the data myself. To handle this I am returning my ModelState with my JSON next to my model.
return JSON(new { model = model, modelState = this.ModelState });
From there I intend to iterate through the modelState and assign errors to the right place with some custom function, but there is one problem.
ModelState looks like this:
"Name",
"Age",
"Hobbies[0].Name",
"Hobbies[0].IsActive",
"Hobbies[1].Name",
"Hobbies[1].IsActive"
I need to separate the [0]'s into an object and [1]'s into their own objects so I can smoothly get the values. This gets confusing for me when I begin to account for a third level of complex object array.
Solution:
var ModelStateConverter = function ($, module) {
module = module || {};
// Convert The ModelState form style object to a standard JS object structure.
module.toObject = function (modelState) {
var ModelState = {};
$.each(modelState, function (key, value) {
AssignValuesToObjectStore(key, ModelState, value);
});
return ModelState;
}
// item is the full identifier ex. "Hobbies[0].Name"
// store is the object we are going to throw arrays, objects, and values into.
// value is the error message we want to get in the right place.
// index is an internal processing parameter for arrays only, setting it's value has no effect.
function AssignValuesToObjectStore(item, store, value, index) {
var periodMatch = item.match(/[\.]/);
if (periodMatch === null) {
if (Array.isArray(store)) {
if (store[index] === undefined) {
store[index] = {};
}
store[index][item] = value;
}
else {
store[item] = value;
}
}
else {
// This wasn't a simple property or end of chain.
var currentProperty = item.slice(0, periodMatch.index); // Get our property name up to the first period.
var container = {}; // We assume we are dealing with an object unless proven to be an array.
var arrayIndex; // This is irrelevant unless we have an array.
if (currentProperty.slice(-1, currentProperty.length) === "]") {
// We are dealing with an array! Hoo Ray?!
arrayIndex = parseInt(currentProperty.slice(currentProperty.indexOf("[") + 1, currentProperty.indexOf("]")));
currentProperty = currentProperty.slice(0, currentProperty.indexOf("[")); // remove the indexer ex. [0] so we are left with the real name
container = []; // We know we need an array instead;
}
if (store[currentProperty] === undefined) {
store[currentProperty] = container; // If this property isn't already created, then do so now.
}
//Recurseive nature here.
AssignValuesToObjectStore(item.slice(periodMatch.index + 1, item.length), store[currentProperty], value, arrayIndex);
}
}
return module;
}($, ModelStateConverter);
You can call this from:
ModelStateConverter.toObject(data.modelState);
Where data.modelState is assumed to be the ModelState from the server.
You could try a library like JSON.NET, or the class JavaScriptSerializer, to serialize the ModelState.
I get undefined whenever I get the value of a property of an object.
function run(id){
var report = services.getReportInfo(id);
var childReport = {
id: newGuid(),
parentId: report.id, // i get undefined
reportPath: report.path // i get undefined
};
...
}
services.js
angular.module('project.services').factory('services', function(){
var reports = [
{
....
},
{
....
}
];
function getReportInfo(id){
var report = reports.filter(function(element){
return element.id === id;
});
};
return{
getReportInfo: getReportInfo
};
}
Whenever I put breakpoint on my var report = services.getReportInfo(id) it could contains the correct values for each property of the my report object. However, when I get the report.id or report.path, I get undefined value.
--Edited--
Oh, I know now where I got wrong.
The getReportInfo function returns an array and I'm accessing the properties without telling from what index should it get the values for the said properties.
function run(id){
var report = services.getReportInfo(id);
var childReport = {
id: newGuid(),
parentId: report[0].id,
reportPath: report[0].path
};
...
}
I placed static index 0, since I know that the array will always have a length of 1.
You are not returning anything from the .factory method and the getReportInfo is also not returning anything. For what you are trying to do, try to use .service method:
angular.module('project.services').service('services', function(){
var reports = [
{
....
},
{
....
}
];
this.getReportInfo = function (id){
var report = reports.filter(function(element){
return element.id === id;
});
return report;
}
}
Here is a good explanation on how to use .factory and .service:
Confused about Service vs Factory
Two immediate issues with the code I can see:
1) Your factory function needs to return a value or constructor function. Right now your code is not initializing the factory to any value.
2) Your getReportInfo function also doesn't return a value, yet you are assigning the function result to a variable.
Read more here: http://docs.angularjs.org/guide/dev_guide.services.creating_services