How to use a logical OR with #if - javascript

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/

Related

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.

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

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.

Edit one line in #each block

I have template:
{{#each action_log}}
<div>
{{Name}}
{{#if editing_score}}
<input type="text" id="set_score" parm="{{_id}}" value="" />
{{else}}
<div class="score" id="{{_id}}">{{Score}} .</div>
{{/if}}
</div>
{{/each}}
And I have a javascript code:
Session.set('editing_score', false);
Template.today_list.editing_score = function() {
return Session.equals('editing_score', true);
};
Template.today_list.events({
'click .score': function(e, t) {
Session.set('editing_score', true);
Session.set('editing_score_id', e.target.id);
Meteor.flush();
}
});
So, user can see list of actions, want to click on one of them and edit some value. But now, if user click on the action, the code display textboxes for all actions.
How can I display a textbox only for one action ?
I have action id in Session:
Session.set('editing_score_id', e.target.id);
But I can not do something like this:
{{#if editing_score && editing_score_id == _id}}
or
{{#if editing_score(_id)}}
What is the best way to do it ?
Thanks in advance.
With your template:
{{#if editing_score id}}
With your helper:
Template.today_list.editing_score = function(id) {
return Session.equals('editing_score_id', id);
};
However, I would refactor a bit so that you only have one Session variable — no need for two.
Also, your 'click .score' passes context, so you can id from this — which I find is more reliable than mucking around with the event that you are passing in.

EmberJS and Handlerbars helpers in a CollectionView

I'm trying to use a collection view and in each item view use a handlerbars helper, but I can't get the helper function to expand my path into the value.
Ember.CollectionView.create({content: App.AController,
itemViewClass: App.ItemView
});
Em.Handlebars.registerHelper('editable', function (path, options) {
options.hash.valueBinding = path;
return Em.Handlebars.helpers.view.call(this, App.EditField, options);
});
<script type="text/x-handlebars" data-template-name="edit-field">
{{#if isEditing}}
{{view Ember.TextField valueBinding = "value" propagatesEvents = true}}
{{else}}
{{#if value}}
{{value}}
{{else}}
<span class="no-name">empty</span>
{{/if}}
{{/if}}
</script>
<script type="text/x-handlebars" data-template-name="item-view">
{{view.content.name}}
{{editable view.content.name}}
</script>
http://jsfiddle.net/epigeon/dNqsV/29/ with full code example.
The 'isEditing' property is on the view, but the context for the collectionView itemView is the content for that view. In order to refer to properties on the view in your template, you have to start the property path with 'view,' as in 'view.isEditing.'
I made that change in your fiddle and the example seems to work as I would expect.

handlebars - is it possible to access parent context in a partial?

I've got a handlebar template that loads a partial for a sub-element.
I would need to access a variable from the parent context in the calling template, from within the partial. .. doesn't seem to resolve to anything inside the partial.
Simplified code goes like this:
the template
{{#each items}}
{{> item-template}}
{{/each}}
the partial
value is {{value}}
(obviously the real code is more complicated but it's the same principle, within the partial .. appears to be undefined.)
To show it's undefined, I've used a very simple helper whatis like this:
Handlebars.registerHelper('whatis', function(param) {
console.log(param);
});
and updated the above code to this:
updated template
{{#each items}}
{{whatis ..}} <-- Console shows the correct parent context
{{> item-template}}
{{/each}}
updated partial
{{whatis ..}} <-- Console shows "undefined"
value is {{value}}
Is there a way to go around that issue? Am I missing something?
EDIT: There's an open issue relating to this question on handlebars' github project
Just in case anyone stumbles across this question. This functionality exists now in Handlebars.
Do this:
{{#each items}}
{{! Will pass the current item in items to your partial }}
{{> item-template this}}
{{/each}}
Working fiddle (inspired by handlebars pull request #385 by AndrewHenderson)
http://jsfiddle.net/QV9em/4/
Handlebars.registerHelper('include', function(options) {
var context = {},
mergeContext = function(obj) {
for(var k in obj)context[k]=obj[k];
};
mergeContext(this);
mergeContext(options.hash);
return options.fn(context);
});
Here's how you'd setup the parent template:
{{#each items}}
{{#include parent=..}}
{{> item-template}}
{{/include}}
{{/each}}
And the partial:
value is {{parent}}
As of 2.0.0 partials now supports passing in values.
{{#each items}}
{{> item-template some_parent_var=../some_parent_var}}
{{/each}}
Took me awhile to find this, hope it's useful for someone else too!
The easiest way to pass the parent context to the partial is to do the loop inside the partial. This way the parent context is passed by default and when you do the loop inside the partial the {{../variable}} convention can access the parent context.
example fiddle here.
The Data
{
color: "#000"
items: [
{ title: "title one" },
{ title: "title two" },
]
}
The Template
<div class="mainTemplate">
Parent Color: {{color}}
{{> partial}}
</div>
The Partial
<div>
{{#each items}}
<div style="color:{{../color}}">
{{title}}
</div>
{{/each}}
</div>
You can use some of the proposed solutions on the comments from the link to github:
https://github.com/wycats/handlebars.js/issues/182#issuecomment-4206666
https://github.com/wycats/handlebars.js/issues/182#issuecomment-4445747
They create helpers to pass the info to the partial.
I created an each Helper function that includes the parent key/values within the subcontext under the key parentContext.
http://jsfiddle.net/AndrewHenderson/kQZpu/1/
Note: Underscore is a dependency.
Handlebars.registerHelper('eachIncludeParent', function ( context, options ) {
var fn = options.fn,
inverse = options.inverse,
ret = "",
_context = [];
$.each(context, function (index, object) {
var _object = $.extend({}, object);
_context.push(_object);
});
if ( _context && _context.length > 0 ) {
for ( var i = 0, j = _context.length; i < j; i++ ) {
_context[i]["parentContext"] = options.hash.parent;
ret = ret + fn(_context[i]);
}
} else {
ret = inverse(this);
}
return ret;
});
To be used as follows:
{{#eachIncludeParent context parent=this}}
{{> yourPartial}}
{{/eachIncludeParent}}
Access parent context values in your partial using {{parentContext.value}}
I needed dynamic form attributes for something like this...
{{#each model.questions}}
<h3>{{text}}</h3>
{{#each answers}}
{{formbuilder ../type id ../id text}}
{{/each}}
{{/each}}
and a helper like so...
Handlebars.registerHelper('formbuilder', function(type, id, qnum, text, options)
{
var q_type = options.contexts[0][type],
a_id = options.contexts[1].id,
q_number = options.contexts[0][qnum],
a_text = options.contexts[1].text;
return new Handlebars.SafeString(
'<input type=' + q_type + ' id=' + a_id + ' name=' + q_number + '>' + a_text + '</input><br/>'
);
});
Which produces...
<input type="checkbox" id="1" name="surveyQ0">First question</input>
My model is a big blob of arrays and objects mixed together. What's noteworthy is that using '../' like so '../type', passes in the parent model as the context, and without it, such as with 'id', it passes in the current model as the context.
To get specifically the parent of the partial (where you may be several partials deep) then follow the other answers like SeanWM.
If you know that the parent is the main template then you can use #root which resolves to the top-most context no matter how deep you are.
e.g. {{#root.rootObject.rootProperty}}
It is a pity that ../../.. does not go up past a partial.

Categories

Resources