What is the proper way to access a nested array of JSON data to be used in Handlebars template file?
My code works in the Dev environment of chrome but doesn't work in Prod.
Here's a sample data of my JSON file.
{
"slider-card-1": [{
"card-img-1": "img/guillermo-rico-1368543-unsplash.jpg",
"card-date-1": "May 29 2019",
"card-title-1": "Deeper dive",
"card-description-1": "Deeper dive into your finances",
"btn-text-1": "Watch video",
"btn-link-1": "vid.co",
"section-header-1": "Be Heard"
},
{
"card-img-2": "img/thought-catalog-685332-unsplash.jpg",
"card-date-2": "May 29 2019",
"card-title-2": "Experience-led Agile",
"card-description-2": "Bringing experiences of our core stakeholders...",
"btn-text-2": "Learn more",
"btn-link-2": "#",
"section-header-2": "Arya"
},
{
"card-img-3": "img/thought-catalog-685332.jpg",
"card-date-3": "Febuary 4 2019",
"card-title-3": "Simplifying terminology",
"card-description-3": "It can be confusing to understand terms.",
"btn-text-3": "Read More",
"btn-link-3": "#",
"section-header-3": "Do it"
}]
}
Code for that goes into the html template file:
<script id="newsletter-template" type="text/x-handlebars-template">
<div>
<div>
<img src={{slider-card-1.[0].card-img-1}}>
</div>
<span>{{slider-card-1.[0].card-date-1}}</span>
<div>
{{slider-card-1.[0].section-header-1}}
</div>
<div>
{{slider-card-1.[0].card-title-1}}
</div>
<div>
{{slider-card-1.[0].card-description-1}}
</div>
<a href={{slider-card-1.[0].btn-link-1}}>
{{slider-card-1.[0].btn-text-1}}
</a>
</div>
</script>
Code to compile the template
<script>
var newsletter_method = {
handlerData: function (resJSON) {
var templateSource = $("#newsletter-template").html(),
template = Handlebars.compile(templateSource),
newsletterHTML = template(resJSON);
$("#my-container").html(newsletterHTML);
},
loadNewsletterData: function () {
$.ajax({
url: "./data.json",
method: 'GET',
success: this.handlerData
})
}
};
$(document).ready(function () {
newsletter_method.loadNewsletterData();
});
</script>
Now when the template is compiled, the generated HTML is blank in the browser. Is there a better way to access the nested array of JSON data in Handlebars? How do I do this?
Related
[
{
title: "title of post"
author: "some guy"
},
{
title: "title of post",
author: "some guy"
}
]
When a user searches for an article, it might return something like the above. How could I make, using VanillaJS or jQuery, it return a page with a <ul> tag with <li> tags for the data inside for the title and author? I know this is a big question, but I don't need an exact answer, just an idea of what to do! I also tried foreach but couldn't figure it out.
Thanks,
Jude Wilson
As far as I understand your question, you're trying to create a <ul> tag that has many children <li> tags each containing the filtered data title, and author keys.
Well normally I recommend using something like vue.js to manage data bindings for your UI, but since you mentioned you want this accomplished using jQuery or vanilla JS, it's rather simple than you think.
Here is a complete HTML code example, using jQuery:
<html>
<body>
<!-- Posts List Container -->
<div>
<ul id="postsContainer"></ul>
</div>
<!-- Post Template (Just an example use-case) -->
<template id="postTemplate">
<li class="post-meta-data">
<h2 class="post-title"></h2>
<strong class="post-author"></strong>
<em class="post-tags"></em>
</li>
</template>
<!-- Include jQuery -->
<script
src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
crossorigin="anonymous"></script>
<!-- Our JS Logic -->
<script>
$(function () {
const
$container = $('#postsContainer'),
// Grabs the HTML content of the "tag" template
template = $('#postTemplate').html(),
// Here you can fetch/return your API's or filtered data
// NOTE: I modified the data to showcase the example.
getData = () => {
return [
{
title: "title of post #1",
author: "some guy",
tags: ['foo', 'bar', 'baz'],
},
{
title: "title of post #2",
author: "some other guy",
tags: ['watermelon', 'apple', 'banana'],
}
];
},
// Here you can call this function to update the UI
// based on the data returned by "getData()"
updateSearchTags = () => {
const postsArray = getData();
// Before adding any new elements to the container, we should empty it first.
$container.empty();
for (const post of postsArray) {
const
{ title, author, tags } = post,
$postEl = $(template);
// Hydrate the template with post data
$postEl.find('.post-title').text(title);
$postEl.find('.post-author').text(author);
$postEl.find('.post-tags').text(tags.join(', '));
// Add the post element to the list
$postEl.appendTo($container);
}
};
// Call the UI Update Function whenever necessary
updateSearchTags();
});
</script>
</body>
</html>
Not too sure what you mean by "return a page" but you could map over the data and set it to the ul with innerHTML
function printData(ele, data) {
ele.innerHTML = data.map(item => `<li>${item.title} - ${item.author}</li>`).join('')
}
const ulEle = document.querySelector('#target')
const data = [
{ title: "title of post", author: "some guy" },
{ title: "title of post", author: "some guy" }
]
printData(ulEle, data)
<ul id="target"></ul>
I have an angular ng-repeat that displays list items inside of an unordered list. Within each list item, I have another unordered list of sub-items. Inside each sub-item <li>, I have a div to display a name and a div to store a value.
I am trying to hook up the ng-mouseenter and ng-mouseleave attributes to the value div, but they aren't firing. Even just trying to execute a console.log statement inside of the directives is not printing anything to the console.
I have no page load errors, everything displays on the page fine so angular is set up correctly, it's just ng-mouseenter and ng-mouseleave are not firing.
What do I have to do in order to get ng-mouseenter and ng-mouseleave to properly fire?
index.html
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.js"></script>
<script src="js/app.js"></script>
<script src="js/SkillsetController.js"></script>
</head>
<body ng-app="mainApp">
<div id="skillset" ng-controller="SkillsetController">
<h2>{{title}}</h2>
<div class="skillListContainer" id="skillListContainer">
<ul class="skillCategoriesList">
<li class="skillCategory" ng-repeat="category in skillsetCategories">
<h3>{{category.title}}</h3>
<ul class="skillsList">
<li class="skill" ng-repeat="skill in category.skills">
<div class="skillName">
<span>{{skill.name}}</span>
</div>
<div class="skill-value" data-skill-value="{{skill.level}}"
ng-mouseenter="console.log('enter');"
ng-mouseleave="console.log('leave');">
{{skill.level}}
</div>
</li>
</ul>
</li>
</ul>
</div>
</div>
</body>
</html>
app.js
var app = angular.module("mainApp", []);
SkillsetController.js
app.controller("SkillsetController", ["$scope", function ($scope) {
$scope.title = "Skillset";
$scope.skillsetCategories = [
{
"title": "Backend",
"skills": [
{
"name": "Java",
"level": 10
}
]
},
{
"title": "Frontend",
"skills": [
{
"name": "HTML",
"level": 9
}
]
},
{
"title": "Frameworks",
"skills": [
{
"name": "jQuery",
"level": 9
}
]
},
{
"title": "Databases",
"skills": [
{
"name": "MySQL",
"level": 10
}
]
}
];
}]);
ng-mouseenter and ng-mouseleave work just fine in your code, just you can't call console.log in html directly, the functions you are using in your html template must be defined in your controller, try move console.log to your controller should resolve the problem.
index.html
<div class="skill-value" data-skill-value="{{skill.level}}"
ng-mouseenter="mouseEnter()"
ng-mouseleave="mouseLeave()">
{{skill.level}}
</div>
controller:
$scope.mouseEnter = function(){
console.log('enter');
};
$scope.mouseLeave = function(){
console.log('leave');
};
console.log() is not valid directly in the html
thats why you need to create those function to work
just change this add this function to your controller
$scope.mouseEnter = function(){
console.log("enter");
};
$scope.mouseLeave = function(){
console.log("leave");
};
and change this in the html
<div class="skill-value" data-skill-value="{{skill.level}}"
ng-mouseenter="mouseEnter()"
ng-mouseleave="mouseLeave()">
{{skill.level}}
</div>
Here is a example
I'm pretty new to using Backbone and Underscore..well Web Development in general.
I'd like to learn how to retrieve individual "model" data on-click from this template format to populate a pop-up modal. Any direction is much appreciated!
Currently, I have a list of projects that is rendered by passing my collection into this template and not using an individual view for each model item. I'm wondering how I can retrieve each project's data to populate a modal on-click.
I've tried getting the html data from e.currentTarget.html but I get undefined
<% _.each(collection, function(model){ %>
<div class="col-md-4 col-sm-6 portfolio-item">
<div class="thumbnail">
<div class="caption">
<h3><a href= <%= model.url %>><%= model.caption %></a></h3>
</div>
<img class="img-responsive" src= <%= model.image %> alt=<%= model.alt %>/>
</div>
<h3 class="project-title text-center"><%= model.title %></h3>
</div><%});%>
I had thought about following the method used in the To-Do List example by Addy Osmani, but I am trying not to have to define a View for the collection of items and a View for the individual models. I can see how this method would be able to assign a click listener for the individual models and pass that model to the modalView render, but again, trying not to do it this way (if possible).
Here is an example of the data
{
"projects": [{
"title": "Portfolio Website",
"caption": "My Showcase",
"dates": " ",
"url": "https://google.ca",
"description": "Lorem Etc Etc",
"image": "picture.jpeg",
"alt": "Portfolio Image"
}, {
"title": "Online Resume",
"caption": "Learn About Me!",
"dates": " ",
"url": "resume.com",
"description": "Look at my resume",
"image": "resume.jpeg",
"alt": "Resume Image"
}, {
"title": "Project",
"caption": "Coming Soon",
"dates": " ",
"url": "",
"description": "Lorem I Don't know what comes after Lorem",
"image": "picture.jpeg",
"alt": "Image"
}]
}
This is the Collection and View I'm using
app.projectCollection = Backbone.Collection.extend({
model: app.projectDetails,
url: '/profile.json',
parse: function(attrs){
return attrs.projects;
}
});
var projects = new app.projectCollection();
projects.fetch();
// View
app.portfolioView = Backbone.View.extend({
el: '.portfolio-body',
projectTemplate: template('portfolio-template'),
initialize: function(options){
this.listenTo(this.collection, 'add', this.render);
},
events: {
'click .portfolio-item': 'showModal' // listen for click to show modal
},
showModal: function(e){
e.preventDefault();
modalView.render(); // render the modal
// console.log("clicked" + e.currentTarget.attr('caption'));
},
render: function(){ // projects render just fine
this.$el.html(this.projectTemplate({collection: this.collection.toJSON()})); // pass in collection data for template to iterate though projects
return this;
}
});
var portfolioView = new app.portfolioView({collection: projects}); // pass in JSON
// Modal View
app.modalView = Backbone.View.extend({
className: 'modal fade',
modalTemplate: template('modal-template'),
attributes: {
tabindex: '-1',
role: 'dialog'
},
render: function(){
this.$el.html(this.modalTemplate()).modal();
return this;
}
});
var modalView = new app.modalView();
Thanks for your time and help!
As I understand, you want to have a link to your model inside template. But we can only have html there. The solution will be to have some unique id for each model.
You can add id to all your models, put it in the template like this
<div class="col-md-4 col-sm-6 portfolio-item" data-id="<%= model.id %>">
get this id in event handler
var id = $(e.currentTarget).data('id');
and get model from collection by this id
var model = this.collection.get(id);
By the way, I highly recommend you to try Marionette, it is super nice view level extension for Backbone.
Suppose I have following html file:
<!DOCTYPE html>
<html>
<head>
<title>LOG</title>
</head>
<body>
<div class="panel panel-success">
<!-- Default panel contents -->
<div class="panel-heading">Log data</div>
<div class="panel-body">
<!-- List group -->
<ul class="list-group">
<li class="list-group-item">Start processing at {{StartProcessing }}</li>
<li class="list-group-item">Finished processing at {{EndProcessing }}</li>
</ul>
<div id="logTvId" kendo-tree-view
k-data-source="treeData">
</div>
</div>
</div>
and following controller code:
Arch.LogController = function ($scope, $resource, $routeParams)
{
var LogResource = $resource('log/:markerId', {}, {
get: {method: "GET", isArray: false}
});
LogResource.get({markerId: $routeParams.markerId}, function (data1)
{
$scope.StartProcessing = new Date(data1.StartProcessing).toLocaleString();
$scope.EndProcessing = new Date(data1.EndProcessing).toLocaleString();
$scope.treeData = new kendo.data.HierarchicalDataSource({ data: [
{ text: "Item 1" },
{ text: "Item 2", items: [
{ text: "SubItem 2.1" },
{ text: "SubItem 2.2" }
] },
{ text: "Item 3" }
]});
});
};
After page load I can see StartProcessing and EndProcessing on page, but I can't see treeview. If I take out code related to $scope.treeData from resource load (say the next instruction after)
then everything works as expeteced. If I add $scope.$apply() to my initial controller code it throws exception...
What I'm doing wrong? Should I deal with promises ($q ??) and wait after resource is loaded?
Thanks in advance.
Ok, actually problem is solved. In resource handler I create kendo treeview by "hand" (via jquery, as usual), so I'm not using kendo-tree-view directive. Simple div with id + jquery.
I have a simple example with Handlebars, and I cannot get it to work! Please help. I am trying to get the google app engine guestbook to work with handlebars instead of jinja2
HTML/Template code is as follows:
Hello {{user.nickname}}
<h2>Top 10 Most Recent Guestbook Entries</h2>
{{#each greetings}}
<br>
<small>[<i>{{date.ctime}}</i>]</small>
<b>
{{#if author}}
<code>{{author.nickname}}</code>
{{else}}
<i>anonymous</i>
{{/if}}
</b>
wrote:
{{comment}}
{{/each}}
</script>
<div id="comment_content"> </div>
<form id="comments" action="">
<div><textarea id="comment" name="content" rows="3" cols="60"></textarea></div>
<div><input id="comment_button" type="button" value="submit"></div>
</form>
{{ url_linktext }}
and here is the JS code:
<script>
$(document).ready(function() {
var source = $("#entry-template");
var srcHTML =source.html();
var template = Handlebars.compile(srcHTML);
// put all your jQuery goodness in here.
$("#comment_button").click(function(event) {
var data = JSON.stringify({
"login": "/_ah/login?continue=http%3A//localhost%3A8080/show",
"logout": "/_ah/login?continue=http%3A//localhost%3A8080/show&action=Logout",
"user": {
"nickname": "dummy",
"email": "dummy#me.com"
},
"greetings": [
{
"comment": "COMMENT1",
"date": {
"ctime": "Sun Feb 10 23:20:21 2013"
},
"author": {
"nickname": "dummy",
"email": "dummy#me.com"
}
}
]
})
var html = template(data);
console.log(html);
$("#comment_content").html(html);
});
});
</script>
Here is how I am referencing Handlebar source
Can someone please help?
Thanks a mil
Your big problem is that you're using JSON.stringify to prepare your data for the compiled template function:
var data = JSON.stringify({ ... });
var html = template(data);
That will end up handing the template function a string (which is a JSON representation of the data you want) when it wants just the object. Drop the JSON.stringify and things will start behaving better:
var data = { ... };
var html = template(data);
Demo: http://jsfiddle.net/ambiguous/Axmbd/
Also, this part of your HTML:
{{ url_linktext }}
doesn't appear to be inside the template's <script> so the {{url}} and {{url_linktext}} won't be filled in. You can fix that by moving that <a> to the right place.