How to render JSON Array using handlebars - javascript

I am trying to render a repeating UI component with Handlebars.js..
<div class="container-fluid article-container">
{{#each this}}
<h1>{{header}}</h1>
<h1>{{url}}</h1>
<h1>{{body}}</h1>
{{/each}}
</div>
The JSON Data coming back from the API Server is given below..
router.get("/api/fetch", function(req, res) {
var respData = [
{
header: "U.S. Releases Surveillance Records of Ex-Trump Aide",
url:
"https://www.nytimes.com/2018/07/21/us/politics/carter-page-fisa.html",
body:
"The release offered a rare glimpse of national security wiretap files and raised echoes of a fight in February over the Russia inquiry between Republicans and Democrats on the House Intelligence Committee."
}
];
res.render("index", respData);
});
The H1 tags are showing up empty in the UI when the browser renders the page.. Can someone please help me out?

For example, if you api data is coming back as this:
var icecreams = [
{ name: "vanilla", price: 10, awesomeness: 3 },
{ name: "chocolate", price: 4, awesomeness: 8 },
{ name: "banana", price: 1, awesomeness: 1 },
{ name: "greentea", price: 5, awesomeness: 7 },
{ name: "jawbreakers", price: 6, awesomeness: 2 },
{ name: "vanilla", price: 10, awesomeness: 3 }
];
Then your handlebar file would be
{{#each ics}}
<p>Flavor: {{name}}</p>
<p>Price: ${{price}}</p>
<p>Awesomeness: {{awesomeness}}</p>
<hr>
{{/each}}
Express route:
app.get("/icecreams", function(req, res) {
res.render("ics", { ics: icecreams });
});
This will render all the ice-creams. For your example, it will just render one.
This example is taken from one of my demos.

Related

AngularJS: get object property in array by matching a different property?

I have an array of products, displayed in a table with an AngularJS ng-repeat.
The products are an array of objects returned from a Wordpress REST API call, and each object has a "category", which returns as a number.
Example: { "name": "Foo", "cat": 12 }
I can't simply bind to the "cat" property, since it displays "12" and I want to display the category label instead.
I can query for the list of all categories, and get an array like so:
[
{ label: 'Customer Engagement Solutions', id: 2 },
{ label: 'Small and Medium Business', id: 13 },
{ label: 'Customer Information Management', id: 4 },
{ label: 'eCommerce', id: 25 },
{ label: 'Location Intelligence', id: 16 },
{ label: 'Enterprise', id: 12 }
]
My product above, "Foo" should display "Enterprise", which is 12.
I know I can bind to a function, as in {{ctrl.getCat(product)}} but I'm not sure how to do the matching of the ID in the product to the one in the category array, and return the category label.
This is trivial to do in actual Wordpress PHP as they provide a function for this very task.
Use Array#find() or even more performant is create a hashmap of the category labels using id as property keys
Using find()
ctrl.getCat = function(product){
let cat = categories.find(e => e.id === product.cat)
return cat ? cat.label : 'Unknown';
}
Or as hashmap:
ctrl.catLabels = categories.reduce((a,c) => { a[c.id] = c.label; return a;},{})
Then in view:
{{ctrl.catLabels[product.cat]}}
The easiest way would be to create a new array of products that already maps the categories. When you initialize the controller with the products and categories, create a new array the maps it.
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
const _categories = [
{ label: 'Customer Engagement Solutions', id: 2 },
{ label: 'Small and Medium Business', id: 13 },
{ label: 'Customer Information Management', id: 4 },
{ label: 'eCommerce', id: 25 },
{ label: 'Location Intelligence', id: 16 },
{ label: 'Enterprise', id: 12 }
];
const _products = [
{ "name": "Foo", "cat": 12 },
{ "name": "Bar", "cat": 16 },
{ "name": "Foo Bar", "cat": 12}
]
let categoryMap = {}
_categories.forEach( (category)=>{
categoryMap[category.id] = category.label;
})
this.products = _products.map( (product)=>{
return {
"name": product.name,
"category": categoryMap[product.cat]
}
})
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.5.x" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js" data-semver="1.5.11"></script>
</head>
<body ng-controller="MainCtrl as ctrl">
<div ng-repeat="product in ctrl.products">
<span>Name: {{product.name}}</span> <span>Category: {{product.category}}</span>
</div>
</body>
</html>

Learning Handlebars: why isn't data being passed to my template if I don't use `this`

I'm learning Handlebars and using this piece of code below after looking at a couple of tutorials. Below, my code won't work if I don't use the keyword this; I tried {{#each shoesData}} and nothing happens to my HTML. If I don't want to use this, it will work only if I change the shoesData to JS Example #2 (following an example from another tutorial that uses the name of an object and not this keyword) and also, in my template wrote {{#each shoes}}. What's the difference?
<h3>The List of Shoes:</h3>
<ul class="shoesNav"></ul>​
<script id="shoe-template" type="text/x-handlebars-template">
{{#each this}}
<li class="shoes">{{name}} -- Price: {{price}}</li>
{{/each}}
</script>
JavaScript #1
var shoesNav = document.getElementsByClassName("shoesNav");
var theTemplateScript = document.getElementById("shoe-template").innerHTML;
var theTemplate = Handlebars.compile(theTemplateScript);
var shoesData =[
{
name: "Nike",
price: 199.00
},
{
name: "Loafers",
price: 59.00
},
{
name: "Wing Tip",
price: 259.00
}
];
var theCompiledTemplate = theTemplate(shoesData);
shoesNav[0].innerHTML = theCompiledTemplate;
JavaScript #2
var shoesData =
{
shoes: [
{
name: "Nike",
price: 199.00
},
{
name: "Loafers",
price: 59.00
},
{
name: "Wing Tip",
price: 259.00
}
]
};
The item you are passing to your template is the array that is referenced by the variable 'shoesData'. So when the template sees it the 'this' object looks like this:
[
{
name: "Nike",
price: 199.00
},
{
name: "Loafers",
price: 59.00
},
{
name: "Wing Tip",
price: 259.00
}
]
In Javascript #2, the object passed to the template is the value referenced by shoesData, which in this case is:
{
shoes: [
{
name: "Nike",
price: 199.00
},
{
name: "Loafers",
price: 59.00
},
{
name: "Wing Tip",
price: 259.00
}
]
};
Do you see the difference? In the second example, the template sees an object with a property of shoes. In your example (Javascript #1) it simply sees an array.
The this parameter in both cases represents what is passed to the template so in your javascript it works with this because it represents an array that can be iterated over by the {{#each ... }}.
To fix this issue to work the way you expect, change the shoesData to look like this:
shoesData = {
shoes:
[
{
name: "Nike",
price: 199.00
},
{
name: "Loafers",
price: 59.00
},
{
name: "Wing Tip",
price: 259.00
}
]
}
Now you can use {{#each shoes}} in your template.
Another way to think of this, if you are still stuck, is what do you see if you do console.log(shoesData) in both examples? This will tell you what the handlebars will see as the this object, and if you do console.log(shoesData.shoes) in the second example, this is what will be iterated over when you do {{#each shoes}}.

How to print json data on view page coming from Server

I am very new to jquery. I have an api '/categories' which returns me json object of categories. Response is like in this format:
[
{
name: "Laptop deals",
slug: "laptop-deals",
imageURL: "image.jpg",
id: 1
},
{
name: "Fashion deals",
slug: "fashion-deals",
imageURL: "image.jpg",
id: 2
},
{
name: "Mobile deals",
slug: "mobile-deals",
imageURL: "image.jpg",
id: 3
},
{
name: "Home & Kitchen deals",
slug: "home-and-kitchen-deals",
imageURL: "image.jpg",
id: 4
},
]
I want to access this in my html view to display.I have tried this:
<script>
$(document).ready(function(){
$("button").click(function(){
$.getJSON('/categories',function(data){
$.each(function(obj,ind,data){
$('#div').append(obj.slug);
});
});
});
});
</script>
</head>
<body>
<button>Get JSON data</button>
<div></div>
</body>
But it doesnt do anything when i click on button. I have tried previous post on stack overflow but nothing works
Change
$.each(function(obj,ind,data){
$('#div').append(obj.slug);
});
into
$.each(data, function(i, item) {
$('div').append(data[i].slug);
//or
//$('div').append(item.slug);
});​

angularjs - cascading selects use model data from first select

I'm attempting to get at the version data stored inside the server model, however, it's not playing ball.
My assumption is that on load the initial data from the first select is not yet available because it hasn't really been selected (technically speaking). I tried to use trigger('click') whether this would help at all, but it did absolutely nothing. I can't seem to find any answers out there and I have a feeling I maybe tackling it from the wrong end.
edit: Forgot to mention, if I have to restructure my data to allow for this to happen, so be it.
Here's all my code + JSFiddle:
http://jsfiddle.net/h8uoy9xr/3/
HTML:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.min.js"></script>
<div ng-app>
<div ng-controller="MyCntrl">
<div class="selection" ng-repeat="selection in selections">
<select ng-model="server" ng-options="server as server.name for server in servers track by server.id" ng-init="server.id=selection.server"></select>
{{ server | json }}
<select ng-model="version" ng-options="version as version.name for version in server.version track by version.id" ng-init="version.id=selection.version"></select>
</div>
</div>
</div>
JS:
function MyCntrl($scope) {
$scope.servers = [
{
"id": 1,
"name": "server1",
"version":
[
{
id:1,
name: "10.x"
},
{
id:3,name: "12.x"
}
]
},
{
"id": 2,
"name": "server2",
"version":
[
{
id: 2,
name: "1.0"
},
{
id: 3,
name: "2.0"
}
]
}
];
$scope.selections = [
{
server: 2,
version: 3
},
{
server: 1,
version: 3
}
];
}
cascading dropdownlist inside ng-repeat
Here's a working solution of the above problem. I hadn't realized angularjs did things a wee bit differently with the models, but finally it clicked. See below code + fiddle for anyone needing something similar in the future:
Fiddle: http://jsfiddle.net/mmy6z57g/2/
Fiddle: http://jsfiddle.net/mmy6z57g/3/ (added a button to demonstrate how additional servers can be added)
HTML:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="MyCntrl">
<div class="selection" ng-repeat="xserver in xservers">
<select ng-model="xservers[$index]" ng-options="server as server.name for server in servers track by server.id"></select>
<select ng-model="xversions[$index]" ng-options="version as version.name for version in xservers[$index].version track by version.id"></select>
</div>
</div>
</div>
JS:
var app = angular.module('test', []);
app.controller('MyCntrl', function($scope) {
$scope.servers = [
{
id: 1,
name: "server1",
version:
[
{
id:1,
name: "10.x"
},
{
id:3,
name: "12.x"
}
]
},
{
id: 2,
name: "server2",
version:
[
{
id: 2,
name: "1.0"
},
{
id: 3,
name: "2.0"
}
]
}
];
$scope.xservers = [
{
id: 2,
name: "server2",
version:
[
{
id: 2,
name: "1.0"
},
{
id: 3,
name: "2.0"
}
]
},
{
id: 1,
name: "server1",
version:
[
{
id:1,
name: "10.x"
},
{
id:3,
name: "12.x"
}
]
}
];
$scope.xversions = [
{
id: 3,
name: "2.0"
},
{
id: 3,
name: "12.x"
}
];
});
Could you use your controller and use a .then(); so that it selects the first one and when that is done then it selects the second? User wouldn't see it and it would solve the issue of second one not getting selected.

Handlebars JS (or EJS) templating: Divide list into categories

I want to create an unordered list based on an array of objects using Handlebars JS.
The objects take the following form:
{ name: 'NameOne', id: 001, category: 2 }, { name: 'NameTwo', id: 002, category: 1 }, { name: 'Name Three', id: 003, category: 1 }, { name: 'Name Four', id: 004, category: 3 }...
At the moment I have a template with an each function which compiles an UL with each object making a new list item, as so:
{{#each this}}
<li>{{ name }}</li>
{{/each}}
What I would like to do is find the simplest way to divide the list depending on categories, so I could end up with something like this...
Category 1
NameTwo
NameThree
Category 2
Name One
Category 3
Name Four
If I can do this only with the template, that would be perfect, but I can also change the JS which compiles the template if needed. I would also be open for using EJS templates if that is a better solution. I have thought of a couple of solutions, but all seem very over-complicated and don't work very well, but surely this can be quite a common problem and there must be good solutions.
Many thanks.
Hi when I'm solving problems like this, I tend to manipulate the data though JavaScript to keep the template clean.
How about trying something like this:
HTML
{{#each list}}
<ul>
<li>
Category {{#key}}
<ul>
{{#each this}}
<li>{{name}}</li>
{{/each}}
</ul>
</li>
</ul>
{{/each}}
JS
var rawData = [
{ name: 'NameOne', id: 001, category: 2 },
{ name: 'NameTwo', id: 002, category: 1 },
{ name: 'Name Three', id: 003, category: 1 },
{ name: 'Name Four', id: 004, category: 3 }
];
var data = {
list:{
}
};
for (var i = 0; i < rawData.length ; i++) {
var a = rawData[i];
var cat = a.category;
if(typeof data['list'][cat] === 'undefined'){
data['list'][cat] = [];
}
data['list'][cat].push(a);
}
Also here is a jsfiddle

Categories