How to iterate a JSON array using Mustache - javascript

I'm new to JSON and mustache. I'm trying to iterate an array that I've created using Mustache and I'm running into some issues. My code looks like this:
var shows=[
{"title":"Strawberry Shortcake","description":"A show about a cake","video":"none","category":"chilren"},
{"title":"Vanilla Ice","description":"A show about a ice","video":"none","category":"adult"}
];
var template="{{#shows}}{{.}}{{/shows}}";
var html=Mustache.render(template,shows);
document.write(html);

You want "shows" to be in a hash in order to properly iterate:
var shows={"shows":[
{"title":"Strawberry Shortcake","description":"A show about a cake","video":"none","category":"chilren"},
{"title":"Vanilla Ice","description":"A show about a ice","video":"none","category":"adult"}
]};
var template="{{#shows}}{{.}}{{/shows}}";
var html=Mustache.render(template,shows);
document.write(html);
This will have the desired effect of producing your template multiple times.
UPDATE
To your question on Lambdas. I just looked this up in the manual. I think it covers what you were asking about:
When the value is a callable object, such as a function or lambda, the
object will be invoked and passed the block of text. The text passed
is the literal block, unrendered. {{tags}} will not have been expanded
- the lambda should do that on its own. In this way you can implement filters or caching.
Template:
{{#wrapped}}
{{name}} is awesome.
{{/wrapped}}
Hash:
{
"name": "Willy",
"wrapped": function() {
return function(text) {
return "<b>" + render(text) + "</b>"
}
}
}

u can do like this:
var shows=[
{"title":"Strawberry Shortcake","description":"A show about a cake","video":"none","category":"chilren"},
{"title":"Vanilla Ice","description":"A show about a ice","video":"none","category":"adult"}
];
var template="{{#.}}title:{{title}},video:{{video}}{{/.}}";
var html=Mustache.render(template,shows);
document.write(html);

Related

foreach Statements with a function

ok I am trying to answer a question and learning to code. regarding an array and using foreach statements the function must remain as this "function animalNames" must stay somewhere but apparently, I am doing something wrong because I get it returned as undefined. even through it produces the correct array back could someone look at it and let me know what i have done wrong.
attached is a picture of the code and array and question that i answered. this is how i wrote my function.
const displayNames = [];
zooAnimals.forEach(function animalNames(element){
var display = "name: " + element.animal_name + ", " + "scientific: " + element.scientific_name
displayNames.push(display);
})
console.log(displayNames);
again i get the correct array back and the data looks correct...but animalNames comes back as undefined...i cannot remove this portion i am to keep it there but i do not know what to do with it.
try this, it defines a function animalNames as separate function
const displayNames = [];
const zooAnimals = [
{animal_name:"Jackel",scientific_name:"Canine"}
]
function animalNames({animal_name, scientific_name}){
return `name: ${animal_name}, scientific: ${scientific_name}`
}
zooAnimals.forEach((element)=>{
displayNames.push(animalNames(element));
})
console.log(displayNames);
I believe you are following this challenge. In this case, make sure to read the instructions properly:
The zoos want to display both the scientific name and the animal name in front of the habitats.
Use animalNames to populate and return the displayNames array with only the animal name and scientific name of each animal.
displayNames will be an array of strings, and each string should follow this pattern: "name: {name}, scientific: {scientific name}"
So from the instructions you are expected to create a function animalNames() that creates an array named displayNames and returns this array from within your function animalNames(). The array displayNames contains strings of the sturcture name: animal_name, scientific: scientific_name.
// you could set zooAnimals as a required parameter in your function
// but as it is defined in the same file you can access it directly as well
function animalNames() {
const displayNames = [];
// you have to do the forEach on zooAnimals here
zooAnimals.forEach(animal => displayNames.push(`name: ${animal.animal_name}, scientific: ${animal.scientific_name}`))
// return an array
return displayNames;
}
The way you did it, the function animalsName() is an anonymous function that only exists within the zooAnimals.forEach() call. Therefore, it is undefined.

Passing in data from Mongodb to HTML table using javascript using Node.js framework

I'm quite new at using node.js. Right now I'm trying to pull data from MongoDB and display it in a table using Javascript + HTML. However, my table is populating with undefined in all the fields. I think something is definitely wrong with how I'm reading data through to the Javascript function b/c I am able to render the full results from the people.js file straight to the webpage. Thank you in advance!! Below is my code:
Code for my people.js file:
exports.getPeople = (req, res) => {
People.find((err, docs) => {
if (err) { return next(err); }
if (docs != null){
console.log(docs.length)
docs.forEach(function(docs, index) {
console.log(index + " key: " + docs.name)
});
res.render('people', { people: docs });
}
else{
res.render('people', { people: docs() });
}
});
};
My Javascript + HTML that's populating my webpage.
script(type='text/javascript', src='http://code.jquery.com/jquery-1.9.1.js', charset='UTF-8')
script.
$(document).ready(function(){
var obj= '$(people)'
var tbl = "<table>"
var content="";
for(i=0; i<obj.length;i++){
content +=
'<tr>+<td>' +obj[i]["name"]+
'</td><td>'+obj[i]["type"]+
'</td><td>'+obj[i]["min_hours"]+
'</td><td>'+obj[i]["max_hours"]+
'</td><td>'+obj[i]["email"]+
'</td><td>'+obj[i]["phone_number"]+
'</td><td>'+ '<input type="button" value = "Update" onClick="Javacsript:deleteRow(this)">' +
'</td><td>'+'<input type="button" value = "Delete" onClick="Javacsript:deleteRow(this)">';
'</td></tr>';
}
content += "</table>"
$('#myTableData').append(content);
});
As you mentioned, you can render the array results from the people.js file directly into the webpage. So, you don't have to read the data through a JavaScript function using jQuery. The template engine language is built on top of JavaScript and it supports plenty of methods and features to do what you're trying to achieve here. So, for example, you may use an iteration method like each..in to build your table (see docs - Iteration):
// ...
body
table(id="myTableData")
// for each person in the people array (from people.js) ...
each person in people
// build a new table row
tr
// insert table data
td #{person.name}
td #{person.type}
td #{person.min_hours}
td #{person.max_hours}
td #{person.email}
td #{person.phone_number}
// add the update and delete buttons
td
input(type="button" value = "Update" onclick=" ... ")
input(type="button" value = "Delete" onclick=" ... ")
// move to next person in the people array ...
The Problem
var obj = '$(people)' does not work as you may expect. You want obj to hold the people array from the people.js file so that you can loop over each object in the array, but this is not what's happening. obj is actually a string value of length 9, so the for loop evaluates 9 string values (not objects). This is why all of your fields are undefined.
To see what I mean, run this code snippet:
var obj = '$(people)';
for (var i = 0; i < obj.length; i++){
console.log(obj[i]);
console.log(obj[i]["name"]);
}
The reason $(people) does not evaluate to an object is mainly because the parent element, script. causes everything below it to evaluate to plain text. The . after the tag causes the template engine to render plain text (see docs: Block in a Tag).
If you wanted to assign people to obj in your inline script you may try it this way:
script
| var obj = #{people};
But this will cause an Unexpected identifier JavaScript error because of the _id field on each item in people. By default _id is an ObjectID hex value from MongoDb so you would have to either remove the _id field from the docs or add quotes to each doc._id so it evaluates to a string. This would all have to be done in person.js before you return the data.
To see what I mean about the Unexpected identifier error, run this code snippet:
// works
var obj = { _id: '583ab33cdaf857b543c76afe',
name: 'john'};
// Error: Unexpected identifier
var obj = { _id: 583ab33cdaf857b543c76afe,
name: 'john'};

Turn Observable Array into nested JSON

I'm having a problem getting an array of information stored properly as JSON.
I made a fiddle to illustrate the problem. Enter a set of tags and take a look at the console to see the output.
More explanation:
So I have an input that takes in a comma-separated list of tags, which I then format.
function createTagArray() {
// given an input value of 'tag1, tag2, tag3'
// returns array = ['tag1', 'tag2', 'tag3']
}
I thought what I needed to do next was the following:
loop over the array and create a 'tag' object for each item which also includes an id for the tag and the id of the contact the tag is associated with.
Each object is pushed to tags, an observable array.
function single_tag(id, contactId, tagLabel) {
var self = this;
self.id = id;
self.contactId = contactId;
self.tagLabel = tagLabel;
}
function createTags() {
var array = createTagArray();
for (var i = 0; i < array.length; i++) {
self.tags().push(new single_tag(uuid.generate(), self.contactId, array[i]));
}
}
Then, I converted it into JSON
self.contactInformation = function() {
return ko.toJS({
"id": self.contactId,
"firstname": self.firstname(),
"lastname": self.lastname(),
... other fields ...
"tags": self.tags(),
})
}
But, when I inspect the console output of calling this function, tags is a collection of arrays, not a nice json object.
How do I get it formatted correctly?
I tried this suggestion, and the tag json is structured correctly, but it is stored with escaped quotes, so that seems wrong.
Thanks for all the help!
I would recommend you knockout.mapping plugin for KO, it allow map complicated JSON structure to view model, even without declarations.
From the documentation
Let’s say you have a JavaScript object that looks like this:
var data = {
name: 'Scot',
children: [
{ id : 1, name : 'Alicw' }
]
}
You can map this to a view model without any problems:
var viewModel = ko.mapping.fromJS(data);
Now, let’s say the data is updated to be without any typos:
var data = {
name: 'Scott',
children: [
{ id : 1, name : 'Alice' }
]
}
Two things have happened here: name was changed from Scot to Scott and children[0].name was changed from Alicw to the typo-free Alice. You can update viewModel based on this new data:
ko.mapping.fromJS(data, viewModel);
And name would have changed as expected. However, in the children array, the child (Alicw) would have been completely removed and a new one (Alice) added. This is not completely what you would have expected. Instead, you would have expected that only the name property of the child was updated from Alicw to Alice, not that the entire child was replaced!
...
To solve this, you can specify which key the mapping plugin should use to determine if an object is new or old. You would set it up like this:
var mapping = {
'children': {
key: function(data) {
return ko.utils.unwrapObservable(data.id);
}
}
}
var viewModel = ko.mapping.fromJS(data, mapping);
In the jsfiddle you were using Knockout 3.0 which doesn't have support for textInput. This was added in 3.2. To use version 3.2 you need to use a cdn such as this: http://cdnjs.com/libraries/knockout
There was typeo in your binding. sumbit should be submit.
There was a problem with your constructor for single_tag. id was not used so I removed it:
function single_tag(contactId, tagLabel) {
var self = this;
self.contactId = contactId;
self.tagLabel = tagLabel;
}
Currently also contactId is not set because the observable has not been set to a value.
To convert to JSON you need to use ko.toJSON instead of ko.toJS:
self.contactInformation = function() {
return ko.toJSON({
"firstname": self.firstname(),
"tags": self.tags(),
})
}
Now when the console writes out an array appears:
{
"firstname":"test",
"tags":[
{"tagLabel":"test1"},
{"tagLabel":"test2"},
{"tagLabel":"test3"}
]
}
JsFiddle
So my problem was more basic than I was realizing. I'm using JSON Server to serve up my data, and I was pulling information from two parts of the database (contacts & tags).
When I tried to update my tags, I was trying to apply them to a property that didn't exist on the contact JSON in my database. Posting the tags separately worked though.

passing formatting JavaScript code to HighCharts with JSON

I have a website that uses AJAX to deliver a JSON formatted string to a HighCharts chart.
You can see this as the middle JSON code part at:
http://jsfiddle.net/1Loag7pv/
$('#container').highcharts(
//JSON Start
{
"plotOptions": {
"series": {"animation": {"duration": 500}}
,"pie": {
"allowPointSelect": true,
"cursor": "pointer",
"dataLabels": {"formatter":function(){return this.point.name+': '+this.percentage.toFixed(1) + '%';}}
}
},
"chart":{"renderTo":"divReportChart"}
,"title":{"text":"Sales Totals"}
,"xAxis":{"title":{"text":"Item"}, "categories":["Taxes","Discounts","NetSalesTotal"], "gridLineWidth":1}
,"yAxis":[{"title":{"text":"Amount"}, "gridLineWidth":1}]
,"series":[{"name":"Amount","type":"pie", "startAngle": -60,"yAxis": 0,"data":[["Taxes",17.8700],["Discounts",36.0000],["NetSalesTotal",377.9500]]}]
}
//JSON end
);
The problem is that the function part...
"dataLabels": {"formatter":function(){return this.point.name+': '+this.percentage.toFixed(1) + '%';}}
is not being transferred via the JSON
All research tells me that there is NO WAY to do this.
IE... Is it valid to define functions in JSON results?
Anybody got an idea on how to get around this limitation?
It is true that you cannot pass functions in JSON. Javascript is a superset of JSON.
A common approach is for the chart to be defined in javascript (e.g. during the page load), and the page then requests just the data via Ajax. When the data is returned it can be added to the chart object, either before it is rendered or afterwards using the highcharts API.
If you really want to pass the formatter function from the server with the chart, send it as a string, and then turn it into a function like this:
var fn = Function(mystring);
and use it in highcharts like:
chart.plotOptions.pie.dataLabels = {"formatter":fn};
I've re-factored your example to show the approach: http://jsfiddle.net/wo7zn0bw/
I had a similar conundrum. I wanted to create the JSON server side (ruby on rails) so I could create images of charts for a web API and also present it on the client web browser with the same code. This is similar to SteveP's answer.
To conform with JSON standards, I changed all formatter functions to strings
{"formatter": "function(){ return this.point.name+':'+this.percentage.toFixed(1) + '%';}"}
On the web side, I navigate the hash looking for formatter keys and replace them with the function using this code (may be a better way!?). javascript:
function HashNavigator(){
this.navigateAndReplace = function(hash, key){
if (!this.isObject(hash)){
//Nice if only navigated hashes and arrays
return;
}
var keys = Object.keys(hash);
for(var i = 0; i< keys.length; i++){
if (keys[i] == key){
//convert string to js function
hash[keys[i]] = this.parseFunction(hash[keys[i]]);
} else if (this.isObject(hash[keys[i]])){
//navigate hash tree
this.navigateAndReplace(hash[keys[i]], key);
} else {
//continue
}
}
};
this.isObject = function(testVar) {
return testVar !== null && typeof testVar === 'object'
}
//http://stackoverflow.com/questions/7650071/is-there-a-way-to-create-a-function-from-a-string-with-javascript
this.parseFunction = function(fstring){
var funcReg = /function *\(([^()]*)\)[ \n\t]*{(.*)}/gmi;
var match = funcReg.exec(fstring.replace(/\n/g, ' '));
if(match) {
return new Function(match[1].split(','), match[2]);
}
return null;
};
}
To use this, would be something similar to this javascript:
hashNavigator = new HashNavigator();
hashNavigator.navigateAndReplace(myHighchartsHash, "formatter")
At that point the hash/js-object is Highcharts ready
Similar idea was used for the web image API.
I was really hoping that hacking at the JSON was not the only solution, but it works!
I used a different approach. I created a JSON like below
{"formatter": "function(){ return this.point.name+':'+this.percentage.toFixed(1) + '%';}"}
When I came to evaluating the expression, I used (assuming that the value of the 'formatter' is formatterValueString)
formatterValueString = formatterValueString.replace('function()', '');
let opts = (new Function(formatterValueString)).call(this);
formatterValue = opts;
The reason to use this approach was it became hard to bind 'this' with the function. The eval() function did not go well with accessing variable this. I am sure there are ways to do it. Just thought this was quick.

covert large array of objects into smaller array objects using javascript

I have an list of values like this from a sql database.
UserName Email ComputerName DateIssued
jjsmith jjsmith#example.com JTComputer 9/14/2013
ltjoseph ltjoseph#example.com LTComputer1 10/21/2013
KTsmith KevinTem#example.com KTComputer1 01/25/2012
ltjoseph ltjoseph#example.com LTComputer2 01/11/2013
KTsmith KevinTem#example.com KTComputer2 01/25/2012
I transform my list into an array of objects.
var user_array = [
{"username":"jjsmith", "email":"jjsmith#example.com", "computerName":"JTComputer", "dateissued":"10/21/2013"}
{"username":"ltjoseph", "email":"ltjoseph#example.com", "computerName":"LTComputer1", "dateissued":"10/21/2013"}
{"username":"KTsmith", "email":"KevinTem#example.com", "computerName":"KTComputer1", "dateissued":"01/25/2012"}
{"username":"ltjoseph", "email":"ltjoseph#example.com", "computerName":"LTComputer2", "dateissued":"01/11/2013"}
{"username":"KTsmith", "email":"KevinTem#example.com", "computerName":"KTComputer2", "dateissued":"01/25/2012"}]
A function has been created by someone else that sends emails to users, it only accepts two parameters which are strings.
So I don't want to send more than 1 email per user. So I am trying to figure out how to combine the items together so that my an example set of strings look like this.
var str1 = "ltjoseph#example.com";
var str2 = "ltjoseph, LTComputer1-10/21/2013, LTComputer2-01/11/2013";
and then fire the other user function to send emails for each of the items in the list.
function sendEmails(str1, str2);
If anyone has any ideas how i can do this. Please advise..
var by_user = {};
for (var i = 0; i < user_array.length; i++) {
if (by_user[user_array[i].username]) {
// Found user, add another computer
by_user[user_array[i].username].str2 += ', ' + user_array[i].computerName + '-' + user_array[i].dateissued;
} else {
// First entry for user, create initial object
by_user[user_array[i].username] = {
str1: user_array[i].email,
str2: user_array[i].username + ', ' + user_array[i].computerName + '-' + user_array[i].dateissued
};
}
}
Now you have the by_user object, which has a single sub-object for each user, whose str1 and str2 properties are the variables you want.
by_user['ltjoseph'].str1 => ltjoseph#example.com
by_user['ltjoseph'].str2 => ltjoseph, LTComputer1-10/21/2013, LTComputer2-01/11/2013
something like this:
var str1=array[0].email
var str2=array[0].username+", "+array[0].computerName+array[0].dateissued
or use a loop and iterate through the array
I strongly recommend bringing in a library like lodash for this sort of thing and using uniq (sample here: http://jsfiddle.net/MwYtU/):
var uniqs = lodash(user_array).pluck('email').uniq().value();
If you're doing javascript and aren't acquainted with lodash or underscore, go do that because it'll save you a lot of time. Using tried and true code is a good idea. Added bonus: if you want to see how the pros are doing something like uniq you can just look at the source code.

Categories

Resources