Compare template helper values in Spacebars {{#if}} block - javascript

I need to compare two template helper values that are located in nested templates. I was wondering if there is an easy way to compare two template helpers (one from the parent template) in an {{#if}} statement like so:
{{#each bids}}
{{#if bid.price===../job.price}}
<span>some text</span>
{{else}}
<span>some other text</span>
{{/if}}
{{/each}}
If you can't do this, I guess the other option is to use Template.parentData in a new template inside the each block? I'm not opposed to this, just the way I outlined above would be much faster and simpler if it's possible. Thanks.

Something like this might work for you:
Template.registerHelper('_', function(){
return _
})
{{#each bids}}
{{#if _.isEqual bid.price ../job.price}}
<span>some text</span>
{{else}}
<span>some other text</span>
{{/if}}
{{/each}}
As a bonus, you not only get _.isEqual, but all _.* functions.

I believe you are right, spacebars doesn't support logical statements in the #if condition. But you can easily define a new block helper that does:
HandlebarsRegister.registerHelper('ifx', function(conditional, options) {
var truthValue = false;
try {
truthValue = eval(conditional);
} catch (e) {
console.log("Exception in #ifx evaluation of condition: ",
conditional, e);
}
if (truthValue) {
return options.fn(this);
} else {
return options.inverse(this);
}
});
and then use
{{#each bids}}
{{#ifx "bid.price===../job.price"}}
<span>some text</span>
{{else}}
<span>some other text</span>
{{/ifx}}
{{/each}}
The only downside is that you will need to write your condition as a string and eval is not exactly elegant. But hey, it works.

Related

Is it possible to provide conditional context in Handlebars.js?

I have a template
<div class="vehicle">
<div class="model">{{model}}</div>
<div class="price">{{price}}</div>
</div>
which is common for some context subparts (I can't change the context), let's call those subparts car and bike, and context has only one of those:
contractContext = {
car?,
bike?,
...
}
I need to make this template work regardless on which subpart is present. I can go:
{{#if car}}
<div class="vehicle">
<div class="model">{{car.model}}</div>
<div class="price">{{car.price}}</div>
</div>
{{else}}
<div class="vehicle">
<div class="model">{{bike.model}}</div>
<div class="price">{{bike.price}}</div>
</div>
{{/if}}
but my real-life templates are considerably bigger, so instead of duplicating those I'd like to use the context conditionally. I've tried:
{{#with car ? car : bike}}
<div class="vehicle">
<div class="model">{{this.model}}</div>
<div class="price">{{this.price}}</div>
</div>
{{/with}}
(and also {{#with car || bike}} which isn't enough for the real-life case because of more complicated checks), but with doesn't work this way (sandbox). Is there some way to set context in a conditional way, or I have to handle this in JS instead?
What you're trying to do is to implement some logic (vehicle selection) in a template. However handlebars and mustache are logicless templates that's why it's not superobvious thing to do in a template. I believe proper way of doing what you want is to have it handled in javascript (where all the logic should be).
Like :
const data = {
vehicle: car || bike
}
And template
<div class="vehicle">
<div class="model">{{vehicle.model}}</div>
<div class="price">{{vehicle.price}}</div>
</div>
Also there's a hacky way of doing it using customHelper. Just to show how cool and customizeable handlebars are:
template:
{{#choseContext car bike}}
<div>{{price}}</div>
<div>{{model}}</div>
{{/choseContext}}
context:
{
bike: {
price: 422,
model: 'foo'
}
}
helper:
Handlebars.registerHelper('choseContext', function(v1, v2, options) {
let context = v1 || v2;
return options.fn(context);
});

Conditional V-HTML rendering

Just wondering if there is something I'm missing here:
<span v-html="(shouldParseHTMLBool ? HTMLContent : '')">
{{ contentWithoutParsedHTML }}
</span>
doens't appear to work.
I would like to have this span interpreting HTML only if shouldParseHTMLBool is set to true.
Is it something possible, or should I use a v-if? In this trivial example it's not a big deal, but with my components I'll have to duplicate ~30 lines for each condition.
It's better to make if condition away from template as much as possible.
You should create a computed object instead.
[template]
<span v-html="computedContent"></span>
[script]
...
computed: {
computedContent: function () {
return this.shouldParseHTMLBool ? this.HTMLContent : ''
}
},
...
The v-html directive replaces the innerHTML of the element. In your case, the {{ contentWithoutParsedHTML }} will be replaced with the value of (shouldParseHTMLBool ? HTMLContent : '')
You can do something like
<template>
<span v-html="conditionalParse"></span>
</template>
<script>
methods: {
conditionalParse: function () {
return this.shouldParseHTMLBool ? this.HTMLContent : ''
}
</script>
try this
<span v-if="shouldParseHTMLBool" v-html="HTMLContentForIfCondition" >All</span>
<span v-else v-html="HTMLContentForElseCondition" ></span>
You can use two spans, One for v-if is true and other for v-else

Escaping curly braces in a label in handlebars

I have handlebar code that prints out a list of labels.
This values are:
{{#each theObj.Options}}
{{#if #last}}
{{this.valueLabel}}
{/if}}
{{#unless #last}}
{{this.valueLabel},
{{/unless}}
{{/each}}.
</p>{{/compare}}
The valueLabels are:
Value A
Value B
{Test}
The handlebar code prints out:
The values are: Value A, Value B, .
I need to get the last {Test} to print out. How do I do that? The printout should be:
The values are: Value A, Value B, {Test}.
Thanks.
Setting this up in a quick Code Pen seemed to yield the desired result. This is with three caveats:
I added an extra '{' to line 7 of your HTML, previously: '{/if}}'.
I removed the closing '{{/compare}}' tag as there was no opening one included in your example.
I created an object 'theObj' with a guess at a reasonable example of what should be included in there.
Could you clarify what is in the 'theObj' object so we can troubleshoot that?
HTML
<div id="app"></div>
<script id="entry-template" type="text/x-handlebars-template">
This values are:
{{#each theObj.Options}}
{{#if #last}}
{{this.valueLabel}}
{{/if}}
{{#unless #last}}
{{this.valueLabel}},
{{/unless}}
{{/each}}.
</script>
JavaScript
var theObj = {
Options: [{valueLabel: 'Value A'}, {valueLabel: 'Value B'}, {valueLabel: '{Test}'}]
};
var source = document.getElementById('entry-template').innerHTML;
var template = Handlebars.compile(source);
var context = {theObj: theObj};
var html = template(context);
document.getElementById('app').innerHTML = html;
console.log(html);
You can check out the Code Pen example here:
See the Pen Escaping curly braces in a label in handlebars by Nicholas Lawrence (#nicholaslawrencestudio) on CodePen.

Computed.alias not updating bind-attr

I recently started using Ember.js. In my small application I currently have problems regarding Ember.computed.alias, because an {{#if}}-section is updated properly, but the bind-attr helper in the same template is not updated accordingly.
The application controller and the action influencing the value look as follows:
App.ApplicationController = Ember.ObjectController.extend({
isEditing: false,
actions: {
toggleEdit: function() {
var a = this.get('isEditing');
this.set('isEditing', !a);
}
}
});
The controller taking care of the template causing problems:
App.CategoriesController = Ember.ArrayController.extend({
needs: ['application'],
isEditing: Ember.computed.alias('controllers.application.isEditing'),
general: function() { // example depending on the alias
var result = this.filterBy('type', 1);
if (!this.get('isEditing')) {
result = result.filterBy('isHidden', false);
}
return result;
}.property('#each.type', '#each.isHidden', 'isEditing'),
// ......
The related template:
<ul id="categories">
{{#if isEditing}}YES!{{else}}NO!{{/if}}
{{#each general}}
<li {{bind-attr class=":general isEditing:editing"}}>
{{name}}
</li>
{{/each}}
</ul>
When the action toggleEdit is triggered, the {{#if}} section is updated and swaps between YES! and NO!, but the editing class is not applied to the list element. I tried encapsulated the alias into another property of the controller depending on the alias, but without success.
I assume it's a beginners mistake, but I can't figure out what I am overlooking.
Thanking you in anticipation.
isEditing is no longer in scope, use controller.isEditing, sorry phone response
Here's an example that would keep it in scope, but I'm fully qualifying it just to show you.
{{#each item in general}}
<li {{bind-attr class=":general controller.isEditing:editing"}}>
{{item.name}}
</li>
{{/each}}

How to use a logical OR with #if

I have data handed to me by the server.
I do not want the container <p> to be present if there is no data item. But there could be several data items or 1 data item, if any exist I need a <p> wrapper.
Ideally, I am looking for some kind of #ifor helper which will accept any number of arguments. Is this possible?
{{#ifor firstname||lastname||nickname||age}}
<p>
{{#if firstname}}
<span class="firtname">{{firstname}}</span>
{{/if}}
{{#if lastname}}
{{lastname}}
{{/if}}
{{#if age}}
Age: {{age}}
{{/if}}
{{#if nickname}}
- ( {{type}} )
{{/if}}
</p>
{{/if}}
A simple solution would be to write a helper that accepts the attributes you wish to test, without the OR operator. You then iterate over the arguments and check if they match a truth test :
Handlebars.registerHelper('ifor', function() {
var l, opts;
opts = Array.prototype.pop.call(arguments);
for (l = arguments.length-1; l>=0; l--) {
// test against undefined if you prefer
//if (typeof(arguments[l])!=='undefined')
if (arguments[l])
return opts.fn(this);
}
return opts.inverse(this);
});
And a Fiddle http://jsfiddle.net/G5Vhc/1/

Categories

Resources