Conditionals in Handlebars.js when fetching data from Google Spreadsheet - javascript

Where I'm At
I'm using Tabletop.js and Handlebars.js to pull all the data from a Google Spreadsheet related to politician statements and then display them onto the page.
Problem
a) I'm having trouble fetching the data for validity from the console from a specific object. When I console.log(data), I'm able to get each of the objects from the Google Spreadsheet, but I haven't been able to get just validity from the first or third object for instance.
Console (single object)
Object {Facts: Tabletop.Model}
+-- Facts: Tabletop.Model
++-- column_names: Array[8]
++-- elements: Array[10]
+++-- 0: Object
+++-- citation1url: "Citation 1-1"
+++-- citation2url: "Citation 2-1"
+++-- citation3url: "Citation 3-1"
+++-- datesaid: "2/20/2015"
+++-- explanation: "Explanation 1"
+++-- name: "Politician 1"
+++-- rowNumber: 1
+++-- statement: "This is my first statement"
+++-- validity: "TRUE"
b) Having syntax issues, wondering if using conditionals in Handlebars would be most efficient way of changing the border color of an element if the validity of a statement (based off data from the spreadsheet) is true, false or unconfirmed vs. writing an if/statement and using jQuery's .css() or .addClass()?
Trying to do something like this (both aren't working)
if (validity == true) {
$(".container").css("border", "1px solid #2ECC40");
} else if (validity == unconfirmed) {
$(".container").css("border", "1px solid #FFDC00");
} else (validity == false) {
$(".container").css("border", "1px solid #FFDC00");
}
OR
{#if Facts.validity.TRUE }}
<div class="trueContainer container">
{{else if Facts.validity.FALSE}}
<div class="falseContainer container">
{{else Facts.validity.UNCONFIRMED}}
<div class="falseContainer container">
{{/if}}
Links
Google Spreadsheet: https://docs.google.com/spreadsheets/d/1glFIExkcuDvhyu5GPMaOesB2SlJNJrSPdBZQxxzMMc4/pubhtml
JSBin: http://jsbin.com/zomopeqoza/1/edit?html,css,output
Relevant Files
scripts (Currently inline)
<!-- This is where the template for facts goes -->
<script id="poli" type="text/x-handlebars-template">
<div class="container">
<p class="claim">Claim: {{name}} has said "{{statement}}"</p>
<p class="explanation">This statement is {{validity}}. This is reason number one and a detailed explanation. <sup class="unconfirmed">1</sup> This is reason number two with a detailed explanation. <sup class="unconfirmed">2</sup> And this is {{explanation}} <sup class="unconfirmed">3</sup> These fact checking boxes should always include 2-3 cited explanation, written by the reporter and linked to a file on DocumentCloud or Soundcloud.</p>
</div>
</script>
var public_spreadsheet_url = "https://docs.google.com/spreadsheets/d/1glFIExkcuDvhyu5GPMaOesB2SlJNJrSPdBZQxxzMMc4/pubhtml";
$(document).ready( function() {
Tabletop.init( { key: public_spreadsheet_url,
callback: showInfo,
parseNumbers: true } );
});
function showInfo(data, tabletop) {
var source = $("#poli").html();
var template = Handlebars.compile(source);
// The actual name of the sheet, not entire .csv
$.each( tabletop.sheets("Facts").all(), function(i, fact) {
var html = template(fact);
// You need an element with this id or class in your HTML
$("#poli-list").append(html);
console.log(data);
});
}
style.css
/*----------------------------------
MAIN STYLES
----------------------------------*/
.trueContainer {
border: 2px solid #2ECC40;
}
.falseContainer {
border: 2px solid #FF4136;
}
.unconfirmedContainer {
border: 2px solid #FFDC00;
}
.container {
margin-top: 1%;
padding: 0.5%;
width: 50%;
}
.claim, sup {
font-family: 'Lato', sans-serif;
font-weight: 900;
}
sup {
padding: 2px;
}
a {
text-decoration: none;
}
.explanation {
font-family: 'Lato', sans-serif;
font-weight: 400;
line-height: 28px;
letter-spacing: 0.025px;
}
.true {
color: #2ECC40;
}
.false {
color: #FF4136
}
.unconfirmed {
color: #FFDC00;
}

Related

Convert index.html to complete Vue CLI structure

I created this app to calculate cube of a number and then cube of the individual elements of the output. I created this Vue js project in JS Fiddle to learn the basics of Vue. But I want to move to complete Vue CLI project. I installed Vue.js in my computer but I can't figure out how to port this single file index.html into the different files in Vue CLI project (App.vue, HelloWorld.vue, main.js). Can anyone tell me how exactly to convert this single page file to the actual project files.
<div id="cubed">
<input v-model='mainNumber' placeholder='Enter a number.'>
<button v-on:click="cube">Submit</button>
<p>
Cube of the number is: {{ this.result }} <br>
</p>
<table id="table">
<tr v-for="row in 2" :key="item">
<td v-for="item in result" v-text="row > 1 ? cubeInt(item) : item"></td>
</tr>
</table>
</div>
#table {
border-collapse: collapse;
width: 50%;
text-align: center;
}
#table td, #customers th {
border: 1px solid #ddd;
padding: 8px;
}
#table tr:nth-child(even){background-color: #f2f2f2;}
#table tr:hover {background-color: #ddd;}
new Vue({
el: "#cubed",
data: {
mainNumber: null,
result: null
},
methods: {
cubeInt: function(int) {
return int*int*int
},
cube: function(event){
var allowedInput = /^[0-9]/;
if (this.mainNumber.match(allowedInput)){
let calc = this.cubeInt(this.mainNumber);
let strToNum = calc.toString().split('').map(Number);
this.result = strToNum
} else {
alert('Only digits are allowed.');
}
},
}
})
So using the Vue cli we have vue files, each file has a
template
script
style (optional)
I recommend checking out: The Project Anatomy section of this site:
https://www.sitepoint.com/vue-cli-intro/
And checkout some tutorials from netninja on youtube, they're really helpful!
If you want to get it working now, but are stuck on importing components etc, as a test, replace and save the HelloWorld.vue file with below your below vue format code:
<template>
<div id="cubed">
<input v-model='mainNumber' placeholder='Enter a number.'>
<button v-on:click="cube">Submit</button>
<p>
Cube of the number is: {{ this.result }} <br>
</p>
<table id="table">
<tr v-for="row in 2" :key="item">
<td v-for="item in result" v-text="row > 1 ? cubeInt(item) : item"></td>
</tr>
</table>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data: {
mainNumber: null,
result: null
},
methods: {
cubeInt: function(int) {
return int*int*int
},
cube: function(event){
var allowedInput = /^[0-9]/;
if (this.mainNumber.match(allowedInput)){
let calc = this.cubeInt(this.mainNumber);
let strToNum = calc.toString().split('').map(Number);
this.result = strToNum
} else {
alert('Only digits are allowed.');
}
},
}
}
</script>
<style scoped>
#table {
border-collapse: collapse;
width: 50%;
text-align: center;
}
#table td, #customers th {
border: 1px solid #ddd;
padding: 8px;
}
#table tr:nth-child(even){background-color: #f2f2f2;}
#table tr:hover {background-color: #ddd;}
</style>

Onsen Cards not showing?

I'm trying to use JavaScript to place cards for however many ids there are in a given JSON input, and while the divisions and cards exist in HTML, they don't show in practice. Can anyone shed some light on this problem?
I'm working on a college project, and for that we need to create an app in Cordova. Our team uses Onsen UI as a framework.
EDIT: It seems as though the division "page__background page--material__background" is obstructing my cards. Basically, they're being rendered behind it. I tried z-index in CSS and that didn't do anything. Anyone know how to get these cards to draw on top.
<ons-page id="policies-page">
<style>
.intro {
text-align: center;
padding: 0 20px;
margin-top: 40px;
}
ons-card {
cursor: pointer;
color: #333;
}
.card__title,
.card--material__title {
font-size: 20px;
}
</style>
<script>
var jojo;
var insertnode1, insertnode2, insertnode3;
function cardGenerator() {
fetch('https://api.thesmokinggnu.net/api/policies')
.then(response => response.json())
.then(data=>{
console.log(data)
// Work with JSON data here
jojo = document.getElementById("policies-page");
for (policy of data.policies){
insertnode2 = document.createElement("DIV");
insertnode2.innerHTML = "<ons-card id='policycards' onclick='fn.pushPage({`id`: `policy_read.html`, `title`: `ID: "+policy.id+"`})'>"+policy.policyname+"</ons-card>"
jojo.appendChild(insertnode2);
}
})
}
cardGenerator();
//<ons-card onclick='fn.pushPage({`id`: `policy_read.html`, `title`: `ID: "+policy.id+"`})'>"+policy.policyname+"</ons-card>
</script>
</ons-page>
</template>```
In theory; this code, with the typical JSON input of 6 ids, should output 6 cards with the ID and other information. Instead, no cards. Not even an error.
Create div inside ons-page and add your cards inside this div:
<ons-page id="policies-page">
<div id="cards"></div>
<style>
...
</style>
<script>
var jojo;
var insertnode1, insertnode2, insertnode3;
function cardGenerator() {
fetch('https://api.thesmokinggnu.net/api/policies')
.then(response => response.json())
.then(data=>{
console.log(data)
// Work with JSON data here
jojo = document.getElementById("cards");
for (policy of data.policies){
insertnode2 = document.createElement("DIV");
insertnode2.innerHTML = "<ons-card ...</ons-card>"
jojo.appendChild(insertnode2)
}
})
}
cardGenerator();
</script>
</ons-page>
Here is a working snippet example:
var jojo;
var insertnode1, insertnode2, insertnode3;
function cardGenerator() {
let data = {policies:[
{id:1, policyname: "policyname1"},
{id:2, policyname: "policyname2"},
{id:3, policyname: "policyname3"}
]}
jojo = document.getElementById("cards");
for (policy of data.policies){
insertnode2 = document.createElement("DIV");
insertnode2.innerHTML = "<ons-card>" + policy.policyname + "</ons-card>"
jojo.appendChild(insertnode2);
}
}
cardGenerator();
.intro {
text-align: center;
padding: 0 20px;
margin-top: 40px;
}
ons-card {
cursor: pointer;
color: #333;
}
.card__title,
.card--material__title {
font-size: 20px;
}
<link href="https://unpkg.com/onsenui/css/onsenui.css" rel="stylesheet"/>
<link href="https://unpkg.com/onsenui/css/onsen-css-components.css" rel="stylesheet"/>
<script src="https://unpkg.com/onsenui/js/onsenui.js"></script>
<ons-page id="policies-page">
<div id='cards'></div>
</ons-page>
Why do we need to do so: when ons-page is compiled it has divs inside. One of them is div with page__content class and it seems like we can see only elements inside this div. When you append to ons-page you append outside of div.page__content. That is why we can't see appended elements.

Using template strings to append HTML

New to es6, is there a way to append HTML using template literals `` in the DOM without overwriting what was currently posted?
I have a huge block of HTML that I need to post for a list that is being created. Where a user is able to post their input.
Every-time the task is submitted it overwrites the current submission. I need it to append underneath.
fiddle for demonstration purpose.
https://jsfiddle.net/uw1o5hyr/5/
<div class = main-content>
<form class ='new-items-create'>
<label>Name:</label><input placeholder=" A Name" id="name">
<button class = "subBtn">Submit</button>
</form>
</div>
<span class="new-name"></span>
JavaScript
form.addEventListener('submit',addItem);
function addItem(event){
event.preventDefault();
let htmlStuff =
`
<div class="main">
<div class="a name">
<span>${name.value}</span>
</div>
<div>
`
itemCreated.innerHTML = htmlStuff;
}
insertAdjacentHTML() adds htmlString in 4 positions see demo. Unlike .innerHTML it never rerenders and destroys the original HTML and references. The only thing .innerHTML does that insertAdjacentHTML() can't is to read HTML. Note: assignment by .innerHTML always destroys everything even when using += operator. See this post
const sec = document.querySelector('section');
sec.insertAdjacentHTML('beforebegin', `<div class='front-element'>Front of Element</div>`)
sec.insertAdjacentHTML('afterbegin', `<div class='before-content'>Before Content</div>`)
sec.insertAdjacentHTML('beforeend', `<div class='after-content'>After Content</div>`)
sec.insertAdjacentHTML('afterend', `<div class='behind-element'>Behind Element</div>`)
* {
outline: 1px solid #000;
}
section {
margin: 20px;
font-size: 1.5rem;
text-align: center;
}
div {
outline-width: 3px;
outline-style: dashed;
height: 50px;
font-size: 1rem;
text-align: center;
}
.front-element {
outline-color: gold;
}
.before-content {
outline-color: blue;
}
.after-content {
outline-color: green;
}
.behind-element {
outline-color: red;
}
<section>CONTENT OF SECTION</section>
You can just use += to append:
document.getElementById('div').innerHTML += 'World';
<div id="div">
Hello
</div>
Element.prototype.appendTemplate = function (html) {
this.insertAdjacentHTML('beforeend', html);
return this.lastChild;
};
If you create the element prototype as per above, you can get the element back as reference so you can continue modifying it:
for (var sectionData of data) {
var section = target.appendTemplate(`<div><h2>${sectionData.hdr}</h2></div>`);
for (var query of sectionData.qs) {
section.appendTemplate(`<div>${query.q}</div>`);
}
}
Depending on how much you're doing, maybe you'd be better off with a templating engine, but this could get you pretty far without the weight.

Apply hover over all list items, except one that is specified

In Angular 2+, is there any way of stopping the hover CSS function on an item of a list, as specified by the Angular code?
I have a stackblitz here to show a simple example what I am talking about.
I am using the ngClass feature to apply style dynamically to whichever list item is selected at the time, as this will change (only one item will be selected at any one time).
<ul>
<li id="{{item.name}}" *ngFor="let item of list" [ngClass]="{disableThis: item.selected}">{{item.name}}</li>
</ul>
I have looked into the :not() feature of CSS application, however I could not find a way of getting this to work with data interpolation.
ie:
.myElement:hover:not({{listItem.name}}) {
background: green;
color: white;
}
app.component.ts
items = {
name: 'name',
lang: ['php', 'javascript', 'angular']
};
app.component.html
<ul>
<li id="{{item.name}}" class="items" *ngFor="let item of items.lang">{{item}}</li>
</ul>
app.component.css or app.component.scss
// 1
.items:not(:first-of-type):hover {
color: red;
}
// 2
.items:not(:last-of-type):hover {
color: red;
}
// 3
.items:not(:first-child):hover {
color: red;
}
// 4
.items:not(:last-child):hover {
color: red;
}
// 5 by index of item
.items:not(:nth-child(2)):hover {
color: red;
}
// 6 by index of item
.items:not(:nth-child(3)):hover {
color: red;
}
By selector Id
app.component.ts
items = [
{
id: 'php',
lang: 'php'
},
{
id: 'angular',
lang: 'angular'
},
{
id: 'css',
lang: 'css'
}
];
app.component.html
<ul>
<li id="{{ item.id }}" class="items" *ngFor="let item of items">{{item.lang}}</li>
</ul>
app.component.css or app.component.scss
.items:not(#angular):hover {
color: red;
}
// Or
.items:not(#php):hover {
color: red;
}
// Or
.items:not(#css):hover {
color: red;
}
If stopping hover depends on particular element having class disableThis attached, you only have to modify your css like this:
.disableThis,
.disableThis:hover {
background: grey;
color: white;
}
#Tushar won the day with a quick comment to say:
The selector should be .myElement:not(.disableThis):hover {. Assuming that myElement class is applied to all li elements.
This worked for me and also in my larger project that I was extracting the code from.

jquery - showing box with if statement by click on button when field is completed - quiz creation

I'm desperately trying to create something very simple for you!
Here's my problem:
I'd like to create a small quiz in which when someone writes anything in a field (), and then click the button "ok" (not sure if I should use a or a ), then 3 possibilities arise (for each case a box appearing under the field input):
The answer is exact and correctly written: then the text will be "Great job!"
The answer is almost correct, meaning that the word is not correctly written (we can define if necessary "almost answers"): the text will be "Almost there..."
The answer is completely wrong, the text will be "Try again!"
Right now I have that:
<body>
<div>
<table>
<tbody>
<tr>
<td>To whom it belongs?</td>
</tr>
<tr>
<td>
<img src="#" alt="Tim's coat" width="100%"/>
</td>
</tr>
<tr>
<td class="answer-box">
<input type="text" class="field-answer" placeholder="Write it there!">
<button id="showresult" class="button-answer" value="Ok">OK</button>
</td>
</tr>
<tr>
<td>
<div class="res" id="switch">Great job!</div>
<div class="res" id="switch2">Almost there...</div>
<div class="res" id="switch3">Try again!</div>
</td>
</tr>
</tbody>
</table>
</div>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script type="text/javascript">
var artist = ["abba"];
var almostartist = ["abaa", "aaba", "aabaa"];
$(document).ready(function(){
$('.field-answer').bind('keyup', function(){
if("#showresult").click(function() {
if($.inArray($(this).val().toLowerCase().trim().replace(/[^\w\s\-\_!##\$%\^\&*\\")\(+=._-]/g, ''), artist) >= 0){
$('#switch').show('good');
}
else if($.inArray($(this).val().toLowerCase().trim().replace(/[^\w\s\-\_!##\$%\^\&*\\")\(+=._-]/g, ''), almostartist) >= 0){
$('#switch2').addClass('soso');
if{
$('#switch3').addClass('non');
}
else {
$('#switch3').removeClass('non');
}
});
});
}
</script>
But of course this is not working...
In case, my CSS is here:
.res {
display: none;
color: white;
font-weight: bold;
text-align: center;
background-color: #490058;
height: 75px;
max-width: 100%;
line-height: 70px;
font-size: 140%;
}
.res.good {
display: block;
}
.res.soso {
display: block;
}
.res.non {
display: block;
}
.answer-box {
text-align: center;
}
.button-answer {
border: none;
background-color: #490058;
color: white;
font-size: 120%;
font-weight: bold;
padding: 8px;
left: 260px;
}
.field-answer {
text-align: center;
border: none;
border-bottom: 2px solid black;
background-color: transparent;
max-width: 230px;
height: 40px;
font-size: 20px;
text-transform: uppercase;
outline: 0;
}
Someone could help me to figure that out, please?
I'm quite sure I'm not far, but cannot solve it...
If you need more precisions on stuffs, please don't hesitate! ;)
Thanks guys!
Baptiste
A slightly different approach - no better than any other suggestion - FIDDLE.
JS
var artist = ["abba"];
var almostartist = ["abaa", "aaba", "aabaa"];
$('.field-answer').focus(function(){
$('.res').css('display', 'none');
$(':input').val('');
});
$('#showresult').on('click', function(){
useranswer = $('.field-answer').val();
useranswer = useranswer.toLowerCase().trim().replace(/[^\w\s\-\_!##\$%\^\&*\\")\(+=._-]/g);
if( $.inArray( useranswer, artist ) === 0 )
{
$('#switch1').css('display', 'block');
}
else if ( $.inArray( useranswer, almostartist ) >= 0 )
{
$('#switch2').css('display', 'block');
}
else //if ( $.inArray( useranswer, almostartist ) < 0 )
{
$('#switch3').css('display', 'block');
}
});
your whole function is bound in to 'keyup' event.
keyup event only occurs once when key is released from pressed.
try deleting bind('keyup', function)
I've found a solution to your problems.
Check this Fiddle
In this script every time you click on the button the field text is compared with the values of the array
Depending on the value of the the corrisponding div is showed.
code
<script type="text/javascript">
$(document).ready(function(){
var artist = ["abba"];
var almostartist = ["abaa", "aaba", "aabaa"];
$("#showresult").click(function() {
var text=$('.field-answer').val();
if(artist.indexOf(text) > -1){
$('#switch').show();
}
else if(almostartist.indexOf(text) > -1){
$('#switch2').show();
}
else{$('#switch3').show();}
});
});
</script>
if you want the message appears on keyup you have to use this code
<script type="text/javascript">
$(document).ready(function(){
var artist = ["abba"];
var almostartist = ["abaa", "aaba", "aabaa"];
$(".field-answer").on('keyup',function() {
$('.res').hide()
var text=$('.field-answer').val().toLowerCase();
if(artist.indexOf(text) > -1){
$('#switch').show();
}
else if(almostartist.indexOf(text) > -1){
$('#switch2').show();
}
else{$('#switch3').show();}
});
});
</script>
If you like one of these solutions remember to falg in green my answer ;) thanks.

Categories