Handlebars.js - If the image_url doesn't contain "questionmark" - handlebars.js

I'm learning Handlebars.js. This is my example:
const url = 'https://api.jikan.moe/anime/1/characters_staff';
fetch(url)
.then(function(response) {
if (!response.ok) {
throw Error(response.statusText);
}
// Read the response as json.
return response.json();
})
.then(function(data) {
// Do stuff with the JSON
console.log(data);
createHtml(data.staff);
})
.catch(function(error) {
console.log('Looks like there was a problem: \n', error);
});
// Handlebars function to generate the HTML
function createHtml(ourData) {
let rawTemplate = document.querySelector("#ourTemplate").innerHTML;
let compiledTemplate = Handlebars.compile(rawTemplate);
let ourGeneratedHTML = compiledTemplate(ourData);
let ourContainer = document.querySelector("#container");
ourContainer.innerHTML = ourGeneratedHTML;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js"></script>
<ul id="container" class="cf"></ul>
<script id="ourTemplate" type="text/x-handlebars-template">
{{#each this}}
<li class='list-container'>
<div class="image-container">
<img src="{{ image_url }}">
</div>
<div class="name-container">
{{ name }}
</div>
</li>
{{/each}}
</script>
Some of the data coming back from the API request doesn't have an image. wherever there's no image, a place holder image is shown, which has the "questionmark" in its name.
I want to achieve that the data should be shown only if the image-name doesn't contain "questionmark" in it.
Is there any way in Handlebars.js to say: Show the data only If the image_url doesn't contain "questionmark"?
Something like this?
{{#each this}}
{{#if image_url !contains "questionmark"}}
<li class='list-container'>
<div class="image-container">
<img src="{{ image_url }}">
</div>
<div class="name-container">
{{ name }}
</div>
</li>
{{/if}}
{{/each}}
Here is the same example on codepen:
Handlebars.js example

EDIT (for non node js use) ---------
You can define a helper to do this with this code:
Handlebars.registerHelper('regex', function (data, regex) {
return (new RegExp(regex).test(data))
})
You would then use it like this:
{{#each this}}
{{#unless (regex image_url 'questionmark')}}
<li class='list-container'>
<div class="image-container">
<a href="{{ url }}" target="_blank">
<img src="{{ image_url }}">
</a>
</div>
<div class="name-container">
{{ name }}
</div>
<div class='role-container'>{{{ role }}}</div>
</li>
{{/unless}}{{/each}}
OLD POST (for node js) -------
There's a useful library called handlebars-helpers (https://github.com/helpers/handlebars-helpers#regex) which provides some regex helpers. You could test the presence of questionmark in the image_url like this:
{{#each this}}
{{#unless (test image_url (toRegex "questionmark"))}}
<li class='list-container'>
<div class="image-container">
<img src="{{ image_url }}">
</div>
<div class="name-container">
{{ name }}
</div>
</li>
{{/unless}}
{{/each}}

Related

Where I can find some of this data bind in Ghost using handlebars?

I know my question seems weird but since I'm new when using ghost with handlebars language, I'm quite confused as to where I can find some of this data bind such as title, #site.logo, etc. I try to find it inside casper template but I still not find it, this is the example of the code image inside casper theme
if you ask why I try to find it, I want to try add some of the content inside it or at least I want to take a look what kind of data bind that casper have or provided.
for example in this post.hbs you can see that there is a lot of data bind in here, but I just can't find what else the data bind they provided,
{{!< default}}
{{!-- The tag above means: insert everything in this file
into the {body} of the default.hbs template --}}
<header class="site-header">
{{> site-header}}
</header>
{{!-- Everything inside the #post tags pulls data from the post --}}
{{#post}}
<main id="site-main" class="site-main outer">
<div class="inner">
<article class="post-full {{post_class}} {{#unless feature_image}}no-image{{/unless}}">
<header class="post-full-header">
{{#if primary_tag}}
<section class="post-full-tags">
{{#primary_tag}}
{{name}}
{{/primary_tag}}
</section>
{{/if}}
<h1 class="post-full-title">{{title}}</h1>
{{#if custom_excerpt}}
<p class="post-full-custom-excerpt">{{custom_excerpt}}</p>
{{/if}}
<div class="post-full-byline">
<section class="post-full-byline-content">
<ul class="author-list">
{{#foreach authors}}
<li class="author-list-item">
<div class="author-card">
{{#if profile_image}}
<img class="author-profile-image" src="{{img_url profile_image size="xs"}}" alt="{{name}}" />
{{else}}
<div class="author-profile-image">{{> "icons/avatar"}}</div>
{{/if}}
<div class="author-info">
{{#if bio}}
<div class="bio">
<h2>{{name}}</h2>
<p>{{bio}}</p>
<p>More posts by {{name}}.</p>
</div>
{{else}}
<h2>{{name}}</h2>
<p>Read more posts by this author.</p>
{{/if}}
</div>
</div>
{{#if profile_image}}
<a href="{{url}}" class="author-avatar">
<img class="author-profile-image" src="{{img_url profile_image size="xs"}}" alt="{{name}}" />
</a>
{{else}}
{{> "icons/avatar"}}
{{/if}}
</li>
{{/foreach}}
</ul>
<section class="post-full-byline-meta">
<h4 class="author-name">{{authors}}</h4>
<div class="byline-meta-content">
<time class="byline-meta-date" datetime="{{date format="YYYY-MM-DD"}}">{{date format="D MMM YYYY"}}</time>
<span class="byline-reading-time"><span class="bull">•</span> {{reading_time}}</span>
</div>
</section>
</section>
</div>
</header>
{{#if feature_image}}
<figure class="post-full-image">
{{!-- This is a responsive image, it loads different sizes depending on device
https://medium.freecodecamp.org/a-guide-to-responsive-images-with-ready-to-use-templates-c400bd65c433 --}}
<img
srcset="{{img_url feature_image size="s"}} 300w,
{{img_url feature_image size="m"}} 600w,
{{img_url feature_image size="l"}} 1000w,
{{img_url feature_image size="xl"}} 2000w"
sizes="(max-width: 800px) 400px,
(max-width: 1170px) 1170px,
2000px"
src="{{img_url feature_image size="xl"}}"
alt="{{title}}"
/>
</figure>
{{/if}}
<section class="post-full-content">
<div class="post-content">
{{content}}
</div>
</section>
{{!-- Email subscribe form at the bottom of the page --}}
{{#if #labs.members}}
{{> subscribe-form}}
{{/if}}
{{!--
<section class="post-full-comments">
If you want to embed comments, this is a good place to do it!
</section>
--}}
</article>
</div>
</main>
{{!-- Links to Previous/Next posts --}}
<aside class="read-next outer">
<div class="inner">
<div class="read-next-feed">
{{#if primary_tag}}
{{#get "posts" filter="tags:{{primary_tag.slug}}+id:-{{id}}" limit="3" as |related_posts|}}
{{#if related_posts}}
<article class="read-next-card">
<header class="read-next-card-header">
{{#../primary_tag}}
<h3><span>More in</span> {{name}}</h3>
{{/../primary_tag}}
</header>
<div class="read-next-card-content">
<ul>
{{#foreach related_posts}}
<li>
<h4>{{title}}</h4>
<div class="read-next-card-meta">
<p><time datetime="{{date format="YYYY-MM-DD"}}">{{date format="D MMM YYYY"}}</time> –
{{reading_time}}</p>
</div>
</li>
{{/foreach}}
</ul>
</div>
<footer class="read-next-card-footer">
<a href="{{#../primary_tag}}{{url}}{{/../primary_tag}}">{{plural meta.pagination.total empty='No posts' singular='% post' plural='See all % posts'}}
→</a>
</footer>
</article>
{{/if}}
{{/get}}
{{/if}}
{{!-- If there's a next post, display it using the same markup included from - partials/post-card.hbs --}}
{{#next_post}}
{{> "post-card"}}
{{/next_post}}
{{!-- If there's a previous post, display it using the same markup included from - partials/post-card.hbs --}}
{{#prev_post}}
{{> "post-card"}}
{{/prev_post}}
</div>
</div>
</aside>
{{/post}}
{{!-- The #contentFor helper here will send everything inside it up to the matching #block helper found in default.hbs --}}
{{#contentFor "scripts"}}
<script>
$(document).ready(function () {
// FitVids - start
var $postContent = $(".post-full-content");
$postContent.fitVids();
// FitVids - end
// Replace nav with title on scroll - start
Casper.stickyNavTitle({
navSelector: '.site-nav-main',
titleSelector: '.post-full-title',
activeClass: 'nav-post-title-active'
});
// Replace nav with title on scroll - end
// Hover on avatar
var hoverTimeout;
$('.author-list-item').hover(function () {
var $this = $(this);
clearTimeout(hoverTimeout);
$('.author-card').removeClass('hovered');
$(this).children('.author-card').addClass('hovered');
}, function () {
var $this = $(this);
hoverTimeout = setTimeout(function () {
$this.children('.author-card').removeClass('hovered');
}, 800);
});
});
</script>
{{/contentFor}}
can someone help me to point it out where I can find a list of data bind they provided? I'm quite confused to understand it
they call that Helpers and you can find the full list here:
Themes documentation
There is several categories: Functionnal, Data, Utility, Extra... And you find several inside with a description for each.
Regards
Are you looking for a particular piece of data that you've put into Ghost admin? {{#site.logo}} and {{#site.title}} is how you out put those data values 😊

Handlebars each/this

I use the Handlebars template engine:
`{{#each classes}}
<div class="col_4">
<h4>{{this.title}}</h4>
<p>{{this.description}}</p>
<a class="button" href="/classes/{{_id}}/details">View Class</a>
</div>
{{/each}}`
Why is it not possible to address title and description as individual fields of this?
If I use "this" like this:
{{#each classes}}
<div class="col_4">
<h4>{{this}}</h4>
<p>{{this}}</p>
<a class="button" href="/classes/{{_id}}/details">View Class</a>
</div>
{{/each}}
all fields are displayed merged.
The following code works with an earlier version:
{{#each classes }}
<div class="col_4">
<h4>{{title}}</h4>
<p>{{description}}</p>
<a class="button" href="/classes/{{_id}}/details">View Class</a>
</div>
{{/each}}

Twig `Variable does not exist` error despite it not being printed

I'm trying to handle non existing variables in my controller which if the database is empty will return a view without any variables:
if(!$votes->findOneById(1) || !$images->findOneById(1)){
return $this->render('admin/stats_and_images.html.twig');
}
return $this->render('admin/stats_and_images.html.twig', [
'images' => $images->countVotesForAllImages(),
'image_podium' => $images->getTopNImages(3),
'form' => $form->createView(),
'votesToday' => $votes->votesToday(),
'votesMonth' => $votes->votesMonth(),
'votesTotal' => $votes->votesTotal()
]);
And in my view I'm trying to handle the lack of variables like so:
{% if (votesTotal[0][1] is defined) and (votesToday[0][1] is defined) and (votesMonth[0][1] is defined) %}
<div class="col-md-6">
<h4 class="sub-section--header">Liczba Oddanych Głosów:</h4>
<hr>
<p>
<div class="col-sm-6">
Dzisiaj:
<span class="text-info large-num">{{ votesToday[0][1] }}</span>
</div>
<div class="col-sm-6">
Ten miesiąc:
<span class="text-info large-num">{{ votesMonth[0][1] }}</span>
</div>
</p>
<p>
<strong>Głosów ogółem: </strong><span class="text-info large-num">{{ votesTotal[0][1] }}</span>
</p>
</div>
<div class="col-md-6">
<h4 class="sub-section--header">Wygrywające zdjęcia:</h4>
<p class="text-muted">Ten miesiąc</p>
<hr>
<div class="row text-center">
<div class="col-md-4">
<a
href="{{ asset("uploads/"~image_podium[0][0].fileName) }}"
target="blank">
<img
src="{{ asset("uploads/"~image_podium[0][0].fileName) | imagine_filter('my_thumb') }}" alt="{{image_podium[0][0].title}}"
class="site-thumbnail"
title="{{image_podium[0][0].title}} - {{image_podium[0][0].author}}">
</a>
<p><strong>Głosów: {{image_podium[0]['votes']}}</strong></p>
</div>
<div class="col-md-4">
<a
href="{{ asset("uploads/"~image_podium[1][0].fileName) }}"
target="blank">
<img
src="{{ asset("uploads/"~image_podium[1][0].fileName) | imagine_filter('my_thumb') }}" alt="{{image_podium[1][0].title}}"
class="site-thumbnail"
title="{{image_podium[1][0].title}} - {{image_podium[1][0].author}}">
</a>
<p>Głosów: {{image_podium[1]['votes']}}</p>
</div>
<div class="col-md-4">
<a
href="{{ asset("uploads/"~image_podium[2][0].fileName) }}"
target="blank">
<img
src="{{ asset("uploads/"~image_podium[2][0].fileName) | imagine_filter('my_thumb') }}" alt="{{image_podium[2][0].title}}"
class="site-thumbnail"
title="{{image_podium[2][0].title}} - {{image_podium[2][0].author}}">
</a>
<p>Głosów: {{image_podium[2]['votes']}}</p>
</div>
</div>
</div>
{% else %}
<h2 class="text-danger text-center">
No votes at the moment :)
</h2>
{% endif %}
But still despite the strict requirement of all three fields to be defined I'm getting this:
Variable "votesToday" does not exist.
And pointing to the <span class="text-info large-num">{{ votesToday[0][1] }}</span> portion of the view.
Why would this be happening? How can it be avoided?
send $votes to twig and check it with {{ dump(votes) }}
If it is a doctrine entity, check your getter. Perhaps is $votes->getVotesToday()

Meter: embedding templates in Blaze

Using Bootstrap and Meteor / Blaze, I am trying to dynamically control the size of a template using a template helper. I'd like to have a button to switch between col-md-4 and col-md-12. The hard-coded column sizing looks like this:
<div class="panel-body">
<div class="row">
{{#each articles}}
<div class="col-md-4">
{{> article this}}
</div>
{{/each}}
</div>
I have a template helper that returns the div and found I needed a closing helper call, or Meteor could complain about an unbalanced <\div>. This seems a bit hacky.
Template.articles.helpers({
format: function() {
return '<div class="col-md-4">';
// return '<div class="col-md-12">';
},
end_format: function() {
return '</div>'
}
});
The markup is:
<div class="panel-body">
<div class="row">
{{#each articles}}
{{{format}}}
{{> article this}}
{{{end_format}}}
{{/each}}
</div>
</div>
But the div tags are returned closed and empty, with the markup I'd like enclosed underneath, as can be seen in this screen shot:
Don't return HTML from template helpers, there is usually a better way.
Why don't you return a dynamic class name from a template helper ?
HTML
<div class="panel-body">
<div class="row">
{{#each articles}}
<div class="{{columnSize}}">
{{> article this}}
</div>
{{/each}}
</div>
<button type="button" class="btn btn-primary js-toggle-column-size">Toggle column size</button>
</div>
ES2015
Template.articles.onCreated(function(){
this.largeColumns = new ReactiveVar(false);
});
Template.articles.helpers({
columnSize(){
const largeColumns = Template.instance().largeColumns.get();
return largeColumns ? 'col-md-12' : 'col-md-4';
}
});
Template.articles.events({
'click .js-toggle-column-size'(event, template){
const largeColumns = template.largeColumns.get();
template.largeColumns.set(!largeColumns);
}
});

How Do I Get First Collection Element Using Spacebars {{#if}} Tag?

I am trying to set the first picture as the "item active" i an Bootstrap Carousel. So, is there a way to make the first element from an collection to be presented different from the rest?
{{#each pictures}}
{{#if #first}}
<div class="item active">
<img src="/pictures/{{fileName}}" alt="" />
</div>
{{else}}
<div class="item">
<img src="/pictures/{{fileName}}" alt="" />
</div>
{{/if}}
{{/each}}
The rendered page only display the content in the {{else}} statement.
Have tried using {{if #first}}, but it does not work for me.
This is pretty similar to the problem of needing an index in your template. You need to map over pictures and mark the one you need treated differently. For example:
Template.myPictures.helpers({
pictures: function() {
return Pictures.find().map(function(picture, index) {
if (index === 0)
picture.isFirst = true;
return picture;
});
}
});
You can then use isFirst in your template like this:
{{#each pictures}}
{{#if isFirst}}
<div class="item active">
<img src="/pictures/{{fileName}}" alt="" />
</div>
{{else}}
<div class="item">
<img src="/pictures/{{fileName}}" alt="" />
</div>
{{/if}}
{{/each}}
Note that CoffeeScript's # doesn't work in templates. To learn more about template contexts see this.
It's not exactly what you were asking but you could make the first item active using jQuery in the rendered callback.
Template.myPictures.rendered = function () {
this.$('.item').first().addClass('active');
};

Resources