Handlebars.js Conditional Statement - javascript

I am trying to execute a conditional statement in my Handlebars template. The issue is it is not rendering the content at all if an if condition is inserted.
Here is my code:
<!DOCTYPE html>
<html>
<head>
<title>Handlebars.js example</title>
</head>
<body>
<div id="placeholder">This will get replaced by handlebars.js</div>
<script type="text/javascript" src="handlebars.js"></script>
<script id="myTemplate" type="text/x-handlebars-template">
{{#names}}
<div style="width:100%;border:2px solid red;">
<table style="width:100%;border:2px solid black">
<tr>
<td style="width:50%; border:2px solid yellow;">
<img src="{{itemImage}}"></img>
</td>
<td style="width:50%; border:2px solid green;">
<img src="btn_downloadAudio.png"></img><br><br>
<img src="btn_downloadPresentation.png"></img><br><br>
<img src="btn_downloadTranscript.png"></img><br><br>
<img src="btn_downloadVideo.png"></img><br><br>
</td>
</tr>
<tr>
<td colspan="2"><img src="{{itemType}}">
<label style="font-weight:bolder">{{itemTitle}}</label>
</td>
</tr>
<tr>
<td colspan="2">
<p>{{itemDescription}}</p>
</td>
</tr>
</table>
</div>
{{/names}}
</script>
<script type="text/javascript">
var source = document.getElementById("myTemplate").innerHTML;
var template = Handlebars.compile(source);
//alert(template);
var data = {
names: [
{ "itemImage": "authorImage.png",
"itemTitle": "Handlebars.js Templating for HTML",
"itemType": "icon_document.png",
"isAudioAvailable": "true",
"isPresentationAvailable": "true",
"isTranscriptAvailable": "true",
"isVideoAvailable": "false",
"itemDescription": "Rendeting HTML content using Javascript is always messy! Why? The HTML to be rendered is unreadable. Its too complex to manage. And - The WORST PART: It does it again and again and again! Loss: Performance, Memory, the DOM has to be re-drawn again each and every time a tag is added."}
]
};
document.getElementById("placeholder").innerHTML = template(data);
</script>
</body>
</html>
CONDITION: Display Video Button if the isVideoAvailable is true
Any help is appreciated.
Thanks,
Ankit

If you want something to be displayed conditionally, you use an {{#if}}:
{{#if isVideoAvailable}}
<img src="btn_downloadVideo.png"><br><br>
{{/if}}
Of course for that to work properly, your data should make sense and isVideoAvailable should be a boolean value. So you'll also need to clean up your data to make sense and isVideoAvailable should be true or false rather than the strings 'true' or 'false'; preprocessing your data is quite common with Handlebars so fixing your data would be the best thing to do, fixing your data would also let you use it in a natural manner in JavaScript.
Demo: http://jsfiddle.net/ambiguous/LjFr4/
But, if you insist on leaving your boolean values as strings then you could add an if_eq helper and say:
{{#if_eq isVideoAvailable "true"}}
<img src="btn_downloadVideo.png"><br><br>
{{/if_eq}}
Cleaning up your data would be a better idea.

Related

Can't get innerHTML to work in one specific spot. I do not understand

I cannot figure out why the function attackGoblin won't write into my HTML (the above function is writing to the exact same space with no issue). I can't get it to write anything at all. The function flee works great, and I feel like it's the exact same thing.
I'm like a week in, and just learning for fun, but this is driving me nuts. Also, I know I shouldn't be writing script in my HTML, but I'm trying to go one step at a time for now. Anybody see what I need to fix? Much appreciated.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Status</title>
<link rel="stylesheet" type="text/css" href="reset.css" />
<link rel="stylesheet" type="text/css" href="style.css" />
<link href="https://fonts.googleapis.com/css?family=Oswald" rel="stylesheet">
</head>
<body>
<header>
<h1>Stupid Game</h1>
</header>
<div class="container">
<table>
<col width="90">
<col width="90">
<col width="600">
<thead>
<th colspan='2'>
<h1>Status</h1>
</th>
<th colspan="2">
<h1>Action
</h1>
</th>
<tr>
<th>
<h2>HP</h2>
</th>
<th>
<h2>
<script>
document.write(you._hp)
</script>
</h2>
</th>
<td id="actionTitle"> Click the door to enter the dungeon.</td>
</tr>
</thead>
<tbody>
<tr>
<td class="left">STR</td>
<td class="left">
<h3>
<script>
document.write(you._str)
</script>
</h3>
</td>
<td id="action" rowspan="5" colspan="4">
<div id="actionLeft" onclick="goblinFight()"><img src="https://images-na.ssl-images-amazon.com/images/I/713sQo9DrQL.png" height="200" width="200">
</div>
<div id="actionMiddle"> </div>
<div id="actionRight"> </div>
<p id="combatMessage">Good Luck!</p>
</td>
</tr>
<tr>
<td class="left">INT</td>
<td>
<h3>
<script>
document.write(you._int)
</script>
</h3>
</td>
</tr>
<tr>
<td class="left">DMG</td>
<td>
<h3>
<script>
document.write(you._dmg)
</script>
</h3>
</td>
</tr>
<tr>
<td class="left">Status Effects</td>
<td>
<h3> - </h3>
</td>
</tr>
<tr>
<td class="left">Gold</td>
<td>
<h3>
<script>
document.write(you._gold)
</script>
</h3>
</td>
</tr>
</tbody>
</table>
</div>
<footer>
</footer>
<script>
function goblinFight() {
document.getElementById("actionTitle").innerHTML = 'A goblin! Click the sword to attack or the door to flee.';
document.getElementById("actionLeft").innerHTML = '<div onclick="attackGoblin()"><img src = "http://iconbug.com/data/80/256/5f0a7369c9651132688f02cbc49a7c28.png" width="120" height="120"></div>';
document.getElementById("actionMiddle").innerHTML = '<img src = "https://marketplace.canva.com/MACg_sB88WY/1/thumbnail_large/canva-young-goblin-illustration-icon-MACg_sB88WY.png" width="240" height="240">';
document.getElementById("actionRight").innerHTML = '<div onclick="flee()"><img src = "http://piskel-resizer.appspot.com/resize?size=200&url=http%3A%2F%2Fwww.piskelapp.com%2Fimg%2F551041a6-7701-11e6-8d38-1bbce077a660.png" width="120" height="120"></div>';
document.getElementById("combatMessage").innerHTML = ''
}
function flee() {
document.getElementById("actionTitle").innerHTML = 'Record what you left with.';
document.getElementById("actionLeft").innerHTML = '<img src = "https://media.giphy.com/media/deWrNpLBRC60E/giphy.gif" width="300" height="300">';
document.getElementById("actionMiddle").innerHTML = '';
document.getElementById("actionRight").innerHTML = '';
document.getElementById("combatMessage").innerHTML = 'Write down anything you left with.';
}
function attackGoblin() {
document.getElementById("combatMessage").innerHTML = 'got him';
}
var you = {
_maxHp: 12,
_hp: 12,
_str: 10,
_int: 10,
_dmg: '5-8',
_gold: 0,
_attack: function() {
return Math.floor(Math.random() * 5 + 4)
}
};
var goblin = {
_hp: 8,
_gold: Math.random(Math.floor() * 4),
_attack: function() {
return Math.floor(Math.random() * 3 + 2);
}
}
After the first run of your goblinFight() function, you end up with the following HTML structure:
<div id="actionLeft" onclick="goblinFight()">
<div onclick="attackGoblin()">
<img src="http://iconbug.com/data/80/256/5f0a7369c9651132688f02cbc49a7c28.png" width="120" height="120">
</div>
</div>
Next, when you click the inner div, you trigger the attackGoblin() function, and it works, but immediately after this the click event bubbles up to the outer div and triggers the goblinFight() function (again), which resets the effect of the first function.
To prevent this, you should pass the reference to the event object into the attackGoblin function (by changing the onclick handler to onclick="attackGoblin(event)") and make the function prevent event propagation:
function attackGoblin(e) {
document.getElementById("combatMessage").innerHTML = 'got him';
e.stopPropagation(); // cancels the bubbling of the event passed into the function
}
Hope this helps!
You need to look in the console. That's where you can tell what kind of error you're getting. Unlike working with most programming languages, if web pages hang due to an error, they will still run what they can and not stick an error window in your face.
If you're using Chrome, the quickest way to look at the console is to right click somewhere on your page and select Inspect. Then click on the Console tab.
I think what it will tell you is that your attackGoblin() function is missing a closing curly brace: }. [EDIT: Since there is a closing curly brace, as Dorado has pointed out, I now think otherwise.]
Good luck. :)

How to display table in same page as the button?The tables are already made up in another html file so I’m just pulling them from there

I have 6 links. I would like for when I click on a link that table shows next to it. I need the table to show in same page as link and not replace it. So I need all tables hidden until i click that link.
For the 6 different tables they each have their own .html file I'm pulling them from.
Heres my code:
function show(nr) {
document.getElementById("table1").style.display = "none";
document.getElementById("table2").style.display = "none";
document.getElementById("table3").style.display = "none";
document.getElementById("table4").style.display = "none";
document.getElementById("table5").style.display = "none";
document.getElementById("table6").style.display = "none";
document.getElementById("table" + nr).style.display = "block";
}
td {
vertical-align: center;
}
#table1,
#table2,
#table3,
#table4,
#table5,
#table6 {
display: none;
}
<h1>BV-Line58 Demi</h1>
<br>
<table>
<tr>
<td>
<a href="bv-line58assets.html" name="table1" onclick='show(1); '>Assets</a>
<br><br>
<a href="bv-line58endpoints.html" name="table2" onclick='show(2);'>Endpoints</a>
<br><br>
<a href="#" name="table3" onclick='show(3);'>IP Search</a>
<br><br>
<a href="#" name="table4" onclick='show(4);'>Non-Malicious Alerts</a>
<br><br>
<a href="bv-line58routes.html" name="table5" onclick='show(5);'>Routes</a>
<br><br>
<a href="#" name="table6" onclick='show(6);'>Suricata Alerts</a>
</td>
<td>
</td>
<td>
<div id="table1"></div>
<div id="table2"></div>
<div id="table3"></div>
<div id="table4"></div>
<div id="table5"></div>
<div id="table6"></div>
</td>
</tr>
</table>
If your need is only to show the tables from a given html page, you can use iframe. That option relies on the fact that you pages are hosted somewhere so you can link to them.
For example:
<iframe width="560" height="315" src="https://www.your-domain.com/bv-line58assets.html" frameborder="0"></iframe>
In your code
<table>
<tr>
<td>
<a href="javascript:void(0)" name="table1" onclick='show(1);>Assets</a>
</td>
<td>
</td>
<td>
<div id="table1">
<iframe width="560" height="315" src="https://www.your-domain.com/bv-line58assets.html" frameborder="0"></iframe>
</div>
</td>
</tr>
</table>
Once each div contains its corresponding iframe, your show(nr) function will show the relevant div, and therefor the relevant iframe (which holds the table, as much as I understand).
Note: this approach is not very comfortable if you actually need to access the data inside the iframe, but for view purposes it should be fine.
There are better ways to handle this, especially if you use a client-side framework like angular or react. This should be a fast and simple way to go with, especially if your tables are not a part of this application (which I am not sure about, as you didn't mention it clearly)
I hope this helps.

How to find next element of particular style after specific button?

I use the following code currently to read data from the first column of the first table:
jQuery(".confluenceTable tr:gt(0) td:nth-child(1)").each(function() {
projects_list.push(jQuery(this).text().trim());
});
Now I need to work with the first table of .confluenceTable located after button with #showdata id. How should I do it?
I tried something like
jQuery("#showdata").nextAll(".confluenceTable tr:gt(0) td:nth-child(1)").each(function() {
but it returns no result.
The html looks like below
<button name="showdata" id="showdata" type="button"><p>show data</p></button>
<p>some text</p>
<div class="table-wrap">
<table class="wrapped relative-table confluenceTable" style="width: 99.94%;">
First, when posting questions which are strongly related to dom querying and manipulation, please include well structured code, you should not make people go jumping unnecessary obstacles to help you.
As for the problem at hand, the main thing missing was :first.
The first snippet is in case the table will be INSIDE <div class="table-wrap"></div>, else the second snippet.
var res = $('#showdata').nextAll('.table-wrap:first').children('.confluenceTable');
var projects_list=[];
res.find("tr:gt(0) td:nth-child(1)").each(function() {
projects_list.push(jQuery(this).text().trim());
});
console.log(projects_list);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button name="showdata" id="showdata" type="button"><p>show data</p></button>
<p>some text</p>
<div class="table-wrap">
<table class="wrapped relative-table confluenceTable" style="width: 99.94%;">
<thead><tr><th>header1</th><th>header2</th></tr></thead>
<tbody><tr><td>cell1</td><td>cell2</td></tr>
<tr><td>cell3</td><td>cell4</td></tr>
<tr><td>cell5</td><td>cell6</td></tr></tbody>
</table>
</div>
var res = $('#showdata').nextAll('.confluenceTable:first');
var projects_list=[];
res.find("tr:gt(0) td:nth-child(1)").each(function() {
projects_list.push(jQuery(this).text().trim());
});
console.log(projects_list);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button name="showdata" id="showdata" type="button"><p>show data</p></button>
<p>some text</p>
<div class="table-wrap">
</div>
<table class="wrapped relative-table confluenceTable" style="width: 99.94%;">
<thead><tr><th>header1</th><th>header2</th></tr></thead>
<tbody><tr><td>cell1</td><td>cell2</td></tr>
<tr><td>cell3</td><td>cell4</td></tr>
<tr><td>cell5</td><td>cell6</td></tr></tbody>
</table>

Modal window for several products

I have a problem. I need to make a modal window for each product, but in the modal window, all products only display the last product. I tried to assign id identifiers, but then only the modal window of the first product works.
window.onload = function() {
$('.img-for-modal').click(function() {
$('.modal').css('display', 'block');
});
$('.close').click(function() {
$('.modal').css('display', 'none');
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="products-wrap">
<table class="product">
<tr>
<td class="img" rowspan="3">
<img class="img-for-modal" src="media/products-image/1.jpg">
<div class="modal">
<span class="close">×</span>
<img class="modal-img" src="media/products-image/1.jpg">
</div>
</td>
<td class="product-info">
<article>Lorem Ipsum is simply dummy text of the printing V1.0</article>
</td>
</tr>
<tr>
<td class="product-info">
<b>Цена: </b>200 руб.
</td>
</tr>
<tr>
<td class="product-delete">
Добавить в корзину
</td>
</tr>
</table>
I suggest you change to this which will show the nearest modal and close the nearest modal. Your current code selects all items with class modal
Note I use jQuery $(function() { ... }); instead of window.onload = function() { ... }
$(function() {
$('.img-for-modal').click(function() {
$(this).next(".modal").show();
});
$('.close').click(function() {
$(this).closest(".modal").hide();
});
});
.modal { display:none}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="products-wrap">
<table class="product">
<tr>
<td class="img" rowspan="3">
<img class="img-for-modal" src="media/products-image/1.jpg">
<div class="modal">
<span class="close">×</span>
<img class="modal-img" src="media/products-image/1.jpg">
</div>
</td>
<td class="product-info">
<article>Lorem Ipsum is simply dummy text of the printing V1.0</article>
</td>
</tr>
<tr>
<td class="product-info">
<b>Цена: </b>200 руб.
</td>
</tr>
<tr>
<td class="product-delete">
Добавить в корзину
</td>
</tr>
</table>
Because you are selecting all the modals on the page and showing them all.
$('.modal').css('display', 'block');
You are not selecting the modal that corresponds with what you picked. In your case the modal is beside the image so grab the next element and display it.
$(this).next('.modal').css('display', 'block');
Other option people tend to do is data attributes and ids. You add a data attribute that has the id of the item you want to show. So you read the attribute, and than show that item.
<img class="img-for-modal" src="media/products-image/1.jpg" data-toggles="#modal1">
<div class="modal" id="modal1">
and than the JavaScript would look at the attribute
var selector = $(this).data("toggles")
$(selector).css('display', 'block');
If you are in need of your modals to display dynamic content, and you are not using a framework like Angular or React to update the components for you - then you will want to use a dedicated modal library. This will help you manage multiple modal instances, as well as all of your display bindings.
I have used Bootbox.js in the past for this purpose - if you are using Bootstrap, check it out. If you are not using Bootstrap, there are other plugins that will help you accomplish the same task.
The trouble with modals is that by design there can only be a single instance at any one time (think Singleton). So each time you call it, you are calling the same instance. So...if you try to populate multiple instances - it wont happen. Only or last data set will be applied.
The problem with using
http://bootboxjs.com/

Angular template mysteriously shows only certain {{expressions}}

I'm using Angular JS v. 1.2.26 and I've spent hours trying to figure out why I cannot get certain expressions to appear in the view.
A complete, minimal, example is below (or jsfiddle here):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script type="text/javascript">
var screen_id = '430732';
</script>
<script type="text/javascript" src="/public/scripts/vendor/angular.min.js"></script>
<script>
var tsApp = angular.module('tsApp',[]);
var screenCtrl = tsApp.controller('screenCtrl', function($scope) {
$scope.columns = [null,[null,{"name":"","id":"583","column":1,"order":1,"display_class":"static-text","block_agency":"static-text","block_mode":"static-text","custom_param":"","custom_body":"<b>This demo screen is brought to you by TransitScreen.com</b>"},{"name":"U St NW+ 17th St NW","id":"591","column":1,"order":2,"display_class":"masstransit","block_agency":"metrobus","block_mode":"bus","stops":[{"agency":"metrobus","mode":"bus","name":"U St NW+ 17th St NW","vehicles":[{"agency":"metrobus","mode":"bus","name":"U St Nw + 17th St Nw","full_route":"96","short_route":"96","destination":"Capitol Heights Station","predictions":[9,42],"direction":"Eastbound","vehicle_number":0,"logo":"","display_route":"96","longname":"96","route_class":"bus_metrobus","destination_class":"bus_metrobus","longname_accessible":null,"prediction1":9,"prediction2":42,"units":"MINUTES"},{"agency":"metrobus","mode":"bus","name":"U St Nw + 17th St Nw","full_route":"90","short_route":"90","destination":"Anacostia Station","predictions":[10,39],"direction":"Southbound","vehicle_number":1,"logo":"","display_route":"90","longname":"90","route_class":"bus_metrobus","destination_class":"bus_metrobus","longname_accessible":null,"prediction1":10,"prediction2":39,"units":"MINUTES"}],"walk_directions":"","walk_minutes":null,"arrow_svg":null}]}]];
});//end screenCtrl
screenCtrl.$inject = ['$scope'];
</script>
</head>
<body class="screen-body total-cols-1" ng-app="tsApp">
<div class="page-holder" ng-controller="screenCtrl">
<div class="col" id="col-1">
<div ng-repeat="block in columns[1]">
<div ng-include="'/public/scripts/block.html'"></div>
</div>
</div>
</div>
</body>
</html>
Here is the contents of block.html:
<div ng-switch on="block.display_class">
<div ng-switch-when="masstransit">
<table>
<div ng-repeat="stop in block.stops">
<div ng-repeat="vehicle in stop.vehicles">
<h2>Test 1</h2>
{{ stop.mode }}
{{ block.display_class }}
{{ vehicle.vehicle_number }}
<tr class="" id="">
<td class="">
<h2>Test 2</h2>
{{ stop.mode }}
{{ block.display_class }}
{{ vehicle.vehicle_number }}
</td>
</tr>
</div><!-- ng-repeat -->
</div><!-- ng-repeat -->
</table>
</div>
</div><!-- end ng-switch -->
Which outputs this...
Test 1 shows the expected values twice (because there are two items in the loop). Then in Test 2 there is only one iteration and only the middle value appears.
The problem is not identified in your code above. There is nothing wrong with ng-repeat in a TR or TD. It is more likely something is wrong in your controller or somewhere else in your code.
I created a jsfiddle which shows an example of displaying data as near the data structure in your question as I could guess. It works fine.
<table ng-controller="myCtrl" border=1>
<tr ng-repeat="stop in block.stops">
<td ng-repeat="vehicle in stop.vehicles">
<dl>
<dt>Mode:</dt><dd>{{ stop.mode }}</dd>
<dt>Class:</dt><dd>{{ block.display_class }}</dd>
<dt>Number:</dt><dd>{{ vehicle.vehicle_number }}</dd>
</dl>
</td>
</tr>
</table>
EDIT
I fixed your jsfiddle to work. See edit 2 here You can't expect Angular to work properly on malformed HTML. You cannot wrap TRs in DIVs like that. Even if Angular works who knows how some browsers might treat it. You might have data popping out after the table. Table rendering is one of the parts of HTML that is fairly strict.

Categories