With handlebars.js I want to display two blocks of html depending of a resulting json.
Let's say I want to thanks my user for ordering items at my shop.
I write my handlerbars.js template like this :
<p>{{name}}</p>
{{#if costIsZero}}
Can't find any order
{{else}}
You bought {{cost}} items in our shop, thanks.
{{/if}}
I'm coding a simple helper for costIsZero like this :
Handlebars.registerHelper('costIsZero', function(){
return this.cost == 0
});
When I mix it with the following json data :
var data = {
"name":"foo",
"cost": 9
};
Whatever the value of "cost" is the {{#if costIsZero}} seems always to be true.
If I comment out the helper itself, thus having nothing for costIsZero it returns always false.
All the code above is available as a JSFiddle there http://jsfiddle.net/gsSyt/
What I'm doing wrong ?
Maybe I'm hijacking the way handlebars.js work, but in that case, How should I implement my feature with handlebars.js ?
Helpers are not invoked when evaluating an expression such as costIsZero.
You could create a custom helper that works as an alternative to if:
Handlebars.registerHelper('ifCostIsZero', function(block) {
if (this.cost == 0) {
return block(this);
} else {
return block.inverse(this);
}
});
Which you would use like this:
{{#ifCostIsZero}}
Can't find any order
{{else}}
You bought {{cost}} items in our shop, thanks.
{{/ifCostIsZero}}
Alternatively, you can use the stock if (or unless) since your test is against zero :
{{#if cost}}
You bought {{cost}} items in our shop, thanks.
{{else}}
Can't find any order
{{/if}}
You can play with both options at http://jsfiddle.net/gsSyt/41/
Try this:
Handlebars.registerHelper('if', function(conditional, block) {
if(this.cost == 0) {
return block(this);
} else {
return block.inverse(this);
}
})
http://jsfiddle.net/mZbtk/2/
Related
I've a data that comes from the database, which I filter and group the item based on the category field and then I display the items with their corresponding category and here's the code I got I'm using EJS as a templating engine :
<%
var catDocs = docs.reduce((acc, x) => {
acc[x.datas.category] = [...(acc[x.datas.category] || []), x];
return acc;
}, {});
Object.entries(catDocs).map(function ([cat, items]){ %>
<h4 class="category"><%=ucfirst(cat)%></h4>
<div class="row">
<%
items.filter(function(item) {
if (!datas.loggedIn && item.datas.status !== 'published') {
return false; // skip
}
return true;
}).map(function (item) {%>
<h5><%= item.datas.status %></h5>
<% });
}); %>
And here's the original data before the reduce :
Now I'm stuck in a bug, some categories does not have any published item and the title still shows because I'm showing the title before I use the filter function, where I skip every item that's not published while the user is not logged in.
If anyone can help to hide the title if there's no item to show (in other words if if there's not any pushblished item and there's is not logged in don't display the corresponding title)
Thank you so much to everyone for helping.
Best Regards
So, you want to filter out only the entries with either truthy loggedIn property, or status "published"? Start with that.
catDocs = docs
.filter(({ datas }) => datas.loggedIn || datas.status === 'published')
.reduce((acc, x) => { // the rest here
BTW, this isn't a good reducer function, mutating the accumulator instead of only returning a new value.
It’s better to prepare the data into structure where it’s easier for your view to render.
E.g. map and filter datas.docs first to new array that has only category=>items pairs where items.length > 0
Also move the !datas.loggedIn check outside of your filter, since you don’t want to render anything if user is not logged in. That’s just normal if clause before anything else in your example.
Hi I want to check the string response in HB. I've tried this:
{{#if status=='false'}}
{{console.log("hi");}}
{{else}}
{{console.log("no");}}
{{#endif}}
How can I check the response simple? I want to show a message if it's true and another message if it's false.
Handlebars is designed to be very simple and doesn't have this functionality out of the box. You should pass the status as a boolean rather than a string, then just use an if statement:
{{#if status}}
{{console.log("hi");}}
{{else}}
{{console.log("no");}}
{{#endif}}
You could also write a helper function:
Handlebars.registerHelper('ifEq', function(a, b, options) {
if (a == b) return options.fn(this)
else return options.inverse(this)
});
Then your handlebars becomes:
{{#ifEq status 'true'}}
Hello
{{else}}
No
{{/ifEq}}
I don't really know how to explain this... I have a collection that has an array in it and when I go through it I've been setting colors.[0].imageLink and not changing the [0], but now I'd like that to be dynamic depending on the value of a function (in this case the function is viewIndex).
Works, but isn't dynamic:
<h3 class='display-price'>$ {{colors.[0].price}}</h3>
What I'd think would work but doesn't:
<h3 class='display-price'>$ {{colors.[(viewIndex)].price}}</h3>
In the corresponding js file (does return 0):
'viewIndex': function() {
console.log(Template.instance().variation.get());
return Template.instance().variation.get();
}
One way to do what you are trying to do is to define a colorPrice helper that takes colors and viewIndex as parameters as follows:
Template.hello.helpers({
colors() {
return [
{ price: 1},
{ price: 2},
{ price: 3}
];
},
viewIndex(){
return 1;
},
colorPrice(colors, viewIndex){
return colors[viewIndex].price;
}
});
Then, in your template you can use it as follows:
<template name="hello">
${{ colorPrice colors viewIndex }}
</template>
So thank you to Kalman for getting me thinking of a dynamic way to do this using more of the javascript! My solution was essentially to use helpers and the "this" keyword by calling helper functions within {{#with item}}, which passed "this" all of the values of the current item it was on, so essentially this became possible:
variationImage() {
return this.colors[Template.instance().variation.get()].imageLink[0];
},
variationLink() {
return this.colors[Template.instance().variation.get()].referralLink;
},
variationPrice() {
return this.colors[Template.instance().variation.get()].price;
}
And at that point getting those values was as simple as using {{variationPrice}} where I needed the price, etc.
{{#with item}}
<h3 class='display-price'>$ {{variationPrice}}</h3>
{{/with}}
And just to add more about how the variations worked each item has a pretty much random number of variations (since we scrape them) so we have to do something along these lines to render how many there are and set which variation you're looking at:
(where colors is an array containing the different color variations of an item)
{{#each colors}}
<option value='{{#index}}'>Variation {{variationIndex #index}}</option>
{{/each}}
(helper function)
variationIndex(index) {
return index+1;
}
(event function to set variation value)
'change .color-variation-select': function(event, template) {
template.variation.set($(event.target).val());
},
I am learning JavaScript so that I can implement Google Tag Manager. I have a list of paths that I would like GTM to rewrite to something friendlier like so:
function() {
return document.location.pathname.indexOf('/l/138281/2016-06-07/dy383') > -1 ? 'Test Success' : undefined;
}
function() {
return document.location.pathname.indexOf('/l/138281/2016-04-03/55z63') > -1 ? 'SPP Contact Success' : undefined;
I'm just not sure how to combine these returns into one function (I currently have about 30 URLs to rewrite). I imagine I can use if/else, but advice would be quite lovely.
--edit--
URL Path Rewrite To
/test-638-jsj /test-success
/spp-zxcv-765 /spp-contact-success
/foo-asdf-123 /foo
/foo-bar-987 /foo-bar
The return function mentioned above does this beautifully for an individual link. I just want to be able to rewrite a series of URLs in one function (or however it makes sense to do this most specifically). Hopefully that helps clarify.
Thanks!
It is always a great idea to structure your code: separate abstract functionality from the specific problem.
What you are actually doing is scannins strings for occurences of keywords and returning specific values if such a keyword has been found.
Therefore, you need a function performing the above computation and a JavaScript datastructure holding your keywords and their values (= Object):
// Return patterns[key] if any key is found in string, else return string:
function match(string, patterns) {
for (key of Object.keys(patterns)) {
if (string.indexOf(key) > -1) return patterns[key];
}
return string;
}
var patterns = {
'/l/138281/2016-06-07/dy383': 'Test Success',
'/l/138281/2016-04-03/55z63': 'SPP Contact Success'
}
console.log(match('/l/138281/2016-06-07/dy383', patterns)); // "Test Success"
console.log(match('/doesnotexist', patterns)); // "/doesnotexist"
console.log(match(document.location.pathname, patterns));
I have a dropdown with values in it. They're accessed through the .value property. I have an ng-repeat on a div that is repeating a lot of data. That data has statuses. When a user selects to filter by a status in the dropdown, I want to filter the ng-repeat by whatever status they chose and the status in the ng-repeat. Here's a better example of what I mean:
data-ng-repeat="stackoverflow in overflows| filter:stackoverflow.property.status===status.value"
In my case, I need to access stackoverflow.property.status and compare it to whatever status is in the drop down.
You'll want to do something like the following:
<span ng-repeat="stackoverflow in overflows | filter: { status : selectedStatus }"></span>
selectedStatus will be the value of ng-model on your dropdown.
You could have something like this
ng-repeat="stackoverflow in overflows | filter: { locations: [{ status: status.value }] }"
Under the circumstances, it ended up just being easier to write my own function and filter by it. In this case, I have a function search that I pass to filter. Works great for me.
I followed an answer that I found on another question. You can view it here: http://jsfiddle.net/suCWn/
$scope.search = function (shop) {
if ($scope.selectedCityId === undefined || $scope.selectedCityId.length === 0) {
return true;
}
var found = false;
angular.forEach(shop.locations, function (location) {
if (location.city_id === parseInt($scope.selectedCityId)) {
found = true;
}
});
return found;
};`
Here's the question: Angularjs filter nested object