Create Javascript objects from a template - javascript

I want to create a javascript object from a template. The problem is I don't know what the template is going to look like beforehand. As a simple example, if I had the template function
template = function (data) {
return {
title: data.title
}
}
then I could run template({ title: "Steve" }) and get back the object
{ title: "Steve" }
Because data.title is not evaluated until I call the template function. But I'm constructing an object based on user input where the field names are not known beforehand and could be deeply nested anywhere in the object.
If I define the object that is returned beforehand then the data.title field in the example would already be evaluated and wouldn't use the input data. For example, I want to be able to define the template object like
obj = { title: this.title }
then redefine the template as
template = function () {
return obj
}
and call template.call({title:"Steve"}). But currently I get back
{ title: undefined }
because this.title was already evaluated when I defined obj. Maybe I'm approaching this the wrong way, because I keep coming to the conclusion that I'd have to modify the function by stringifying it, modifying the string to include the unevaluated code this.title and creating a new function from the string. But that seems like a plain awful idea.
And traversing the object looking for special values to replace seems expensive and complicated. I also looked for some sort of javascript object templating library but didn't find anything.
EDIT: To make it more clear that the input data and the template structure won't necessarily match, I may want have a template that looks like
template = function (data) {
return {
name: "Alfred",
stats: {
age: 32,
position: {
level: 10,
title: data.title
}
}
}
}
and call template({title:"Manager"}) to get
{
"name": "Alfred",
"stats": {
"age": 32,
"position": {
"level": 10,
"title": "Manager"
}
}
}

So I've managed to solve this by (ab)using functions as metadata to mark the values that should be replaced in the template. This is made possible by two things:
I only need valid JSON values, so I can safely say that functions aren't literal user input
JSON.stringify has a replacer parameter which will traverse the object and can be used to pass the input data to the template
Using a template generator like this
var templateMaker = function (object) {
return function (context) {
var replacer = function (key, val) {
if (typeof val === 'function') {
return context[val()]
}
return val;
}
return JSON.parse(JSON.stringify(obj, replacer))
}
}
I create a template object, replacing field names with functions that return the field name
var obj = {
name: "Alfred",
stats: {
age: 32,
position: {
title: function () { return 'title' },
level: function () { return 'level' }
}
}
}
then I create the template function, define my input, and render it to an object
var template = templateMaker(obj);
var data = {
title: "Manager",
level: 10
}
var rendered = template(data);
and magically, the object output looks like
{
"name": "Alfred",
"stats": {
"age": 32,
"position": {
"title": "Manager",
"level": 10
}
}
}

Maybe template engines like Mustache would help you with this.
You can define your object template in string:
var template = '{ title: {{title}} }';
then render it with the data, and convert it to json:
var data = {title: 'I am title'};
var obj = JSON.parse(Mustache.render(template, data));
UPDATE:
I read your updated example, here is the corresponding example:
var template = JSON.stringify({
name: "Alfred",
stats: {
age: 32,
position: {
level: 10,
title: '{{title}}'
}
}
});
var data = {title: 'I am title'};
var obj = JSON.parse(Mustache.render(template, data));
obj.stats.position.title == "I am title";

Related

How can I get variable data in the object prototype?

I have an array of objects, and I want to create a method which returns the data inside the method. How do I do this? Thanks
// for example, I have this data
var data = [{ number: 1, name: "Bob" }, { number: 2, name: "Jim" }];
Object.prototype.number = () => {
// how do I access the object data inside this prototype
// i am trying to return something like this, but it doesnt work:
// return this.number;
};
// i want this to return the number 1
data[0].number();
^
| I am trying to access this data in the prototype function and return the number
I know this can be done with data[0].number, but I am trying to do it with an object prototype method.
You need to:
Use different property names for the property holding the method and the property holding the data
Not use an arrow function (which has lexical this)
// for example, I have this data
var data = [{
xnumber: 1,
name: "Bob"
}, {
xnumber: 2,
name: "Jim"
}];
Object.prototype.number = function() {
return this.xnumber;
};
// i want this to return the number 1
console.log(data[0].number());
Note that editing the prototype of built-in objects is considered dangerous.

How can I store localStorage data in one object?

Now I got something like this, instead of creating new-info: {"name": "name", "description": "mydescription"} it deletes the previous new-info and just adds for example
new-info: "test" How can I make this to be one object of values?
function setName(value) {
this.name = value
localStorage.setItem('new-info', JSON.stringify(this.name))
},
function setDescription(value) {
this.description = value
localStorage.setItem('new-info', JSON.stringify(this.description))
},
The issue appears to be that you are not assigning the required object to localStorage, but rather the string property, meaning you are overwriting the key new-info with a string. Try saving the entire object instead, like this:
const info = {
name: '',
description: ''
};
function setName(value) {
info.name = value;
saveToStorage();
};
function setDescription(value) {
info.description = value;
saveToStorage();
};
function saveToStorage() {
localStorage.setItem('new-info', JSON.stringify(info));
}

Objects with and without quotation marks

Edit: using google apps script, these are objects that are passed back from their functions. When I say logged I mean the result of the return function is logged in GAS.
I have objects that serve as profiles for a larger script, and I was trying to generate a larger profile programmatically.
When called and logged:
[ { name: "a1",
functionName:"functionA",
options:{something:"a1run"}
},
{ name: "a2",
functionName:"functionA",
options:{something:"a2run"}
},
{ name: "a3",
functionName:"functionA",
options:{something:"a3run"}
}
]
Shows up in the log as this:
[{
functionName = functionA,
name = a1,
options = {
something = a1run
}
},
}, {
functionName = functionA,
name = a2,
options = {
something = a2run
}
}, {
functionName = functionA,
name = a3,
options = {
something = a3run
}
}]
you'll note that all of the quotation marks disappeared.
Yet when I call an almost identical function where I generated each part of the object with a for loop (this)
var s1 = "";
for (var i=0; i<5;i++)
{
var newString = '';
newString += '{ name: "a'+i+'",';
newString += 'functionName: "functionA",';
newString += 'options:{something: "a'+i+'run"} },';
s1+= newString;
}//for loop
The logged result of the function is this:
[{
name: "a0",
functionName: "functionA",
options: {
something: "a0run"
}
}, {
name: "a1",
functionName: "functionA",
options: {
something: "a1run"
}
}, {
name: "a2",
functionName: "functionA",
options: {
something: "a2run"
}
}, {
name: "a3",
functionName: "functionA",
options: {
something: "a3run"
}
}, {
name: "a4",
functionName: "functionA",
options: {
something: "a4run"
}
}, ]
This is a problem because the initial formatting does work as a profile, and the second one does not. What aspect of JavaScript objects do I need to understand? I didn't think it would make a difference because this object goes through a JSON.stringify when it is used but I was wrong.
My question isn't just how I change it so that it is processed the same way, but why one is being treated differently from the other.
This is not the correct way of creating a JSON array
you should do something like this and forget about creating a string of JSON array
let output = [];
for (var i = 0; i < 5; i++) {
output.push({
name: "a" + i,
functionName: "functionA",
options: {
something: "a" + i + "run"
}
});
}
console.log(output);
Instead of using Logger or the Google Apps Script built-id debugger to "print" your JSON in order to debug it if you are able to use Stackdriver use it or use the HTML Service to print the JSON object to your web browser console instead.
The above becase the Log view (View > Logs), as you already found, not always print JSON objects correctly.
If you want to use Logger, first you should convert to JSON object to string. In most cases using JSON.stringify(...) will work fine.
References
https://json.org/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON

Create tags object from array in mongo collection for Select2 options

I have a mongo collection with a array field called 'tags'. What I want to do is create a single object that stores all of the various tags with a label and value. The end result should be an object I can use in a Select2 field in a Meteor application to create the results options. I have gotten close, but all of my solutions have not worked and are super ugly (read: not functional javascript)
Here is a sample document:
{
"_id": "sjkjladlj",
"title": "Coldplay is Cool",
"tags": ["music", "yuppie"]
}
Now the end result I would like is:
[
{
value: "music",
label: "music"
},
{
value: "yuppies",
label: "yuppies"
},
{
value: "Some tag from another doc"
label: "Some tag from another doc"
}
]
Any ideas?
Here is the closest I have gotten.
options: function() {
tagsArray = [];
ca = Notes.find({}, {tags: 1}).fetch();
ca.forEach(function(it) {
result = {};
result = it.tags;
tagsArray.push(result);
});
console.log(tagsArray);
return tagsArray;
}
}
you can try with aggregation pipeline like this
db.colleaction.aggregate([{$project:{_id:0,tags:1}},{$unwind:"$tags"},{$project:{"value":"$tags","lable":"$tags"}}])
Update. As soon as I posted I realized I simply need to add a inner loop. Its ugly, but it works.
options: function() {
tagsArray = [];
ca = Notes.find({}, {tags: 1}).fetch();
ca.forEach(function(it) {
result = {};
result = it.tags;
result.forEach(function(child){
inner = {};
inner.value = child;
inner.label = child;
tagsArray.push(inner);
});
});
console.log(tagsArray);
return tagsArray;
}

prototype JSON to Object

The following is part of a JSON string returned from the server:
{
col1: {
caption: 'Workspace',
combodata: {
c_0: {
id: 0,
value: 'Filter...'
},
c_1: {
id: 1,
value: 'Tax'
},
c_2: {
id: 2,
value: 'HR'
}
}
}
}
After eval, I can access .caption, and .combodata is visible in Firebug as an object, with c_0 and c_1 visible as objects inside .combodata, with id and value in both c_0 and c_1.
How do I step through each object in .combodata? I tried .combodata.each(c), but that throws an exception. I won't know the names of the objects in .combodata during run time.
You can use a regular for loop for that:
for(var key in obj.col1.combodata) {
var combo_obj = obj.col1.combodata[key];
...
}
Can I suggest that you do not eval() the JSON that's returned? What you should be doing is:
var jsondata = { ... };
var obj = JSON.parse(jsondata);
The reason is because eval'ing a string can be dangerous. Imagine if your JSON data looked like this:
"{ some json data here }; alert(document.cookie)"
When you eval that, the users cookie is displayed to them. Now think what happens if instead of alert, that cookie is posted to an attackers URL. They now have access to that users account if such exists.
if
var result = {col1: { caption: 'Workspace',combodata: {c_0: {id: 0,value: 'Filter...'},c_1: {id: 1, value: 'Tax'},c_2: {id: 2, value: 'HR'}}}};
then
for ( i in result.col1.combodata ) {
var item = result.col1.combodata[i];
//Do stuff with item
}
I have found the following to work as well and will use this:
Object.values(col1.combodata).each(function(c2) {
id = c2.id;
});

Categories

Resources