Using Handlebars how do I wrap 4 divs in a row div? - javascript

I had a hard time wording the question so I apologize. I'm using handlebars to generate listings from a JSON file and I'm stuck. Basically every 4 cards I generate need to be wrapped in a row div. Here's what I tried but didn't work very well
(using coffeescript)
Handlebars.registerHelper "everyOther", (index, amount, scope) ->
if index % amount
scope.inverse this
else
scope.fn this
Here's my template
{{#each data}}
{{#everyOther #index 4}}
<div class = "card-result-row">
{{/everyOther}}
<div class = "card-result with-image">
<img src="{{this.userImgUrl}}" alt="Contacts Image" />
<div class="contact-info">
{{this.user}}
<span class="contact-title">{{this.jobTitle}}</span>
<span class="contact-email">{{this.email}}</span>
<span class="meta-location">{{this.location}}</span>
</div>
</div>
{{#everyOther #index 4}}
</div>
{{/everyOther}}
{{/each}}
So basically on the first iteration I want it to open a row div and after the 4th "card" is generated I want to close out that row and start a new one. Thanks for the help

What are you trying to do is not a good practice in Handlebars as in other logic-less templaters. It will be much better if you make data transformation before passing it to template, instead of inventing such strange helpers.
If you try to group your data first, for example using underscore.js groupBy:
data = _.toArray(
_.groupBy(data, function (item, index) {
return Math.floor(index/4);
})
);
Then your template will look like:
{{#each data}}
<div class = "card-result-row">
{{#each this}}
<div class = "card-result with-image">
<img src="{{userImgUrl}}" alt="Contacts Image" />
<div class="contact-info">
{{user}}
<span class="contact-title">{{jobTitle}}</span>
<span class="contact-email">{{email}}</span>
<span class="meta-location">{{location}}</span>
</div>
</div>
{{/each}}
</div>
{{/each}}
This template cleaner and much easy to read, isn't it?

Related

How to get the first class from two same classes with cheerio

currently i'm making a web scraper with node.Js using cheerio. i want to get the data inside the first of class="panel-items-list.
the html look like this
<div class="margin-bottom-=30">
<div class="side-list-panel">
<ul class="panel-item-list">...</ul>
</div>
</div>
<div class="margin-bottom-=30">
<div class="side-list-panel">
<ul class="panel-item-list">...</ul>
</div>
</div>
i already did like this
const relateArticle = $('.panel-items-list').map((i, section)=>{
let articles = $(section).find('h5');
return articles.text();
}).get();
but it return the data from both class="panel-items-list". how do i get data just from one class ? sorry my english is bad. thanks in advance !
To get the first class only from the .panel-item-list use .get(0) that means you are only selecting the first index found using .map
Also, in your current code jQuery your are not selecting the right class selector.
In addition use .trim() method when getting the text to clean up any unseen spaces within the text.
Live Working Demo:
const relateArticle = $('.panel-item-list').map((i, section) => {
let articles = $(section).find('h5');
return articles.text().trim();
}).get(0)
console.log(relateArticle) //Foo
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="margin-bottom-=30">
<div class="side-list-panel">
<ul class="panel-item-list">
<h5>
Foo
</h5>
</ul>
</div>
</div>
<div class="margin-bottom-=30">
<div class="side-list-panel">
<ul class="panel-item-list">
<h5>
Bar
</h5>
</ul>
</div>
</div>
Use first():
$('.panel-items-list').first().find('h5').text()

Bigcommerce - Display Certain Message/Image on a specific Category/Product

I'm fairly new to Big Commerce and had a question on how I'd be able to display an image / certain message on a specific category.
I currently have a free shipping image being displayed on all products but there's a certain category that I do not want that snippet of code being displayed.
How would I be able to achieve this?
The way I have the free shipping image being displayed is by modifying the card.html file
templates > components > products > card.html
Thanks!
You can consider inserting the image via banner on specific categories.
After this you can inject css via jQuery or override with plain css to display the banner wherever you like.
yourstore.xyz/manage/marketing/banners/create
Edit 1
Wrapping your div to if also should solve your issue on displaying that image in certain products.
{{#if product.shipping}}
{{#if product.shipping.price.value '===' 0}}
<img class="yourImgClass">{{imgURL}}
{{/if}}
{{/if}}
Edit 2
Here's the JS way to remove some elements from your specific categories:
var url = location.href;
if ( url.indexOf( 'your-url' ) !== -1 ) {
document.getElementById('your-free-shipping-image-id').remove();
console.log("yay, image bye");
}
else {
console.log("nay, image stays");
}
Edit 3
This is sort of a shift-make workaround with limited knowledge on jQuery. This would be much easier if you could place ID's on image wrapping span elements but I think you'll be alright with this.
Place this on footer, inside brackets.
$(document).ready(function() {
var s = $('img[src*="https://cdn7.bigcommerce.com/s-7iywz/product_images/uploaded_images/free-shipping-banner.jpg"]');
var url = location.href;
console.log(s.length);
if ( url.indexOf( 'framed' ) !== -1 ) {
s.remove();
}
});
You could try exempting that category (via ID or name) in particular with a conditional statement.
As an example, you might try:
{{#unless category.id '==' 5321}}
Free shipping!
{{/unless}}
Or for if/else:
{{#if category.id '==' 5321}}
No free shipping!
{{else}
Free shipping!
{{/if}}
Thanks for that but it still doesn't seem to be working.
Also there was a glitch with your code
You had {{/unless}} instead of {{/if}}
So this is what I used:
{{#if category.id '==' 70}}
No free shipping!
{{else}}
Free shipping!
{{/if}}
I've attached the Category ID which is 70
Everything on the store still shows the Free Shipping
If you want to remove "Free Shipping" from single category then use below code and get it fixed.
{{#if id '!=' 70 }} Free Shipping Message}} {{/if}}
If it doesn't work for you then i suggest you to post the "Card File" code here so that i can check what exactly you have written in the code.
<article class="card {{#if alternate}}card--alternate{{/if}}">
<figure class="card-figure">
{{#or price.non_sale_price_with_tax price.non_sale_price_without_tax}}
{{#if theme_settings.product_sale_badges '===' 'sash'}}
<div class="sale-flag-sash">
<span class="sale-text">On Sale!</span>
</div>
{{else if theme_settings.product_sale_badges '===' 'topleft'}}
<div class="sale-flag-side">
<span class="sale-text">On Sale!</span>
</div>
{{else if theme_settings.product_sale_badges '===' 'burst'}}
<div class="starwrap">
<div class="sale-text-burst">On Sale!</div>
<div class="sale-flag-star"></div>
</div>
{{/if}}
{{/or}}
{{#if demo}}
<img class="card-image lazyload" data-sizes="auto" src="{{cdn 'img/loading.svg'}}" data-src="{{getImage image 'productgallery_size' (cdn theme_settings.default_image_product)}}" alt="{{image.alt}}" title="{{image.alt}}">
{{else}}
<div class="card-image-container">
<a href="{{url}}">
<img class="card-image lazyload" data-sizes="auto" src="{{cdn 'img/loading.svg'}}" data-src="{{getImage image 'productgallery_size' (cdn theme_settings.default_image_product)}}" alt="{{image.alt}}" title="{{image.alt}}">
</a>
</div>
{{/if}}
</figure>
<div class="card-body">
<h4 class="card-title">
{{#if demo}}
{{name}}
{{else}}
{{name}}
{{/if}}
</h4>
<div class="card-text" data-test-info-type="price">
{{#or customer (if theme_settings.restrict_to_login '!==' true)}}
{{> components/products/price price=price customer=customer}}
{{else}}
{{> components/common/login-for-pricing}}
{{/or}}
</div>
<p class="card-text" data-test-info-type="productRating">
<span class="rating--small">
{{> components/products/ratings rating=rating}} <span style="color:gray;">{{#if num_reviews '>' 0}}</span>
({{num_reviews}}) Review{{#if num_reviews '>' 1}}s
{{/if}}{{/if}}
</span>
</p>
{{#if id '!=' 70 }}
<span><img src="https://cdn7.bigcommerce.com/s- 7iywz/product_images/uploaded_images/free-shipping-banner.jpg?t=1525811996&_ga=2.165904830.1847420277.1525706943-52562068.1494873040"></span>
{{/if}}
</div>
</article>
i have modified your code, just copy it and paste it in your theme.

Select element with jquery inside a handlebars each iteration

I'm building for exercise a e-shop. I'm displaying a list of product in the HTML, using handlebars.
For every element I'm adding a button that should allow the user to add the item in the shopping cart. It's not working because every time I click on the button I add all the list and not only the single object.
So I know that I'm passing all the list (because I pass 'list' as argument), I was wondering how pass only the current item for every button. I'm not sure how to do it.
html:
<div class="container-fluid">
<script id="list-items" type="text/x-handlebars-template">​
{{#each list}}
<div class="items col-lg-3 col-md-4 col-sm-6 col-xs-12">
Category: {{ category}} </br>
Name: {{ name }} </br>
Price: {{ price }} </br>
Quantity: {{ quantity }}
<button class="addItem" class="btn">Buy</button>
</div>
{{/each}}
</script>
</div>
javascript:
var ShoppingCart = function () {
this.cart = [];
};
ShoppingCart.prototype.addItem = function(item) {
this.cart.push(item);
};
shoppingCart = new ShoppingCart();
$('.addItem').click(function() {
shoppingCart.addItem(list);
});
In your click function you add exactly the whole list:
shoppingCart.addItem(list);
And you do not need to transfer the whole item to be added. As you already have the whole list in the scope, you just need to let your function know what was the selected index.
Possible solution could be to add index by handlebars into button id and then parse it in jQuery and proceed or consider replacing jQuery way to subscribe to on-click event with pure javascript solution and insert the index of the item withe help of handlebars, in the way:
<button class="addItem" class="btn" onclick="addItem({{#index}})">Buy</button>
Where addItem is:
function addItem(index) {
shoppingCart.addItem(list[index]);
}
I guess you could sort this out with an item ID (or index as pointed out elsewhere) :
<button id="{{ #index }}" class="addItem" class="btn">Buy</button>
$('.addItem').click(function() {
shoppingCart.addItem(list[$(this).id]);
});

Ember JS Enable dropdown only on element which was clicked

I am an Ember newbie and can't get my head around how to run events on specific elements populated via records on my ember model.
Here is my Template
{{#each user in model }}
{{#each activecredit in user.activecredits}}
<div class="col-lg-2 hive-credit-box active-credit">
<div class="credit-brandname">
{{activecredit.Brandname}}
</div>
<div class="credit-brand-image-container">
<img src="http://localhost:3000/{{activecredit.Imglocation}}" class="credit-image"/>
</div>
<div class="hive-credit-percent"><img class="hive-filled-container" src="imgs/hivefilled9.png"/></div>
<div class="hive-credit-dollars">$xx.xx</div>
<div {{bind-attr class=activecredit.Brandname}} {{action 'enableTrade'}}><img src="imgs/trade_button.png" class="credit-trade-button" /></div>
<div class="credit-brand-amount">
xx%
</div>
<!-- Trade button click dropdown -->
{{#if isSelected}}
<div class="hivetrade">
<div class="arrow_box">
Hi there
</div>
<div class=""></div>
</div>
{{/if}}
</div>
{{/each}}
{{/each}}
Now I want to show a drop down on each element on click of a button .
When I set the enableTrade action , all dropdowns of all the divs show up.
actions:{
enableTrade:function(){
this.set('isSelected',true);
}
}
How do I enable only the dropdown in the particular div that was created.
I suppose I need to bind certain attributes to each div,but how do I access which div was clicked in my controller?
Any help would be appreciated .
Thanks
You can pass params through the action helper, and use them to set isSelected on the appropriate item.
{{#each model as |user| }}
{{#each user.activeCredits as |activeCredit|}}
<button {{action 'enableTrade' activeCredit}}>Enable Trade</button>
{{#if activeCredit.isSelected}}
<div class="hivetrade">Hello World</div>
{{/if}}
{{/each}}
{{/each}}
To handle it:
actions:{
enableTrade:function(credit) {
credit.set('isSelected',true);
}
}
If you need to allow only one credit to be selected at a time, your controller and action could be modified like this:
selectedCredit: null,
actions:{
enableTrade:function(credit) {
// unselect the previously selected credit
if (this.get('selectedCredit')) {
this.set('selectedCredit.isSelected', false);
}
// select, and cache the selection choice
credit.set('isSelected',true);
this.set('selectedCredit', credit);
}
}

Limit number of list items to be displayed in List expression

I have a requirement of displaying a list array in two divs, first half in first DIV and second half list in second DIV. Please not that I don't want to splice the list array in two parts and display them in two lists.
Here is the template I have, for example
<!-- First half number of items should be displayed in firstHalf div -->
<div id="firstHalf">
{{#each names:i}}
{{firstName}} {{lastName}}
{{/each}}
</div>
<!-- Second half number of items should be displayed in secondHalf div -->
<div id="secondHalf">
{{#each names:i}}
{{firstName}} {{lastName}}
{{/each}}
</div>
For example If I have data like below
var data = [ {
firstName : "Joe", lastName: "Armstrong"
}, {
firstName : "Jose", lastName: "Valim"
}];
It needs to be rendered like below
<div id="firstHalf">
Joe Armstrong
</div>
<div id="firstHalf">
Jose Valim
</div>
Please give me suggestions to proceed further.
Another valid approach would be to reference list members indirectly, via an index reference. In this example we're generating a range of indices, and then referring to list[this]:
<!-- First half number of items should be displayed in firstHalf div -->
<div id="firstHalf">
{{#each range(0,names.length/2) }}
{{names[this].firstName}} {{names[this].lastName}}
{{/each}}
</div>
<!-- Second half number of items should be displayed in secondHalf div -->
<div id="secondHalf">
{{#each range(names.length/2,names.length) }}
{{names[this].firstName}} {{names[this].lastName}}
{{/each}}
</div>
The range() function just looks like this:
function ( start, end ) {
var arr = [];
for ( i = Math.floor(start); i < Math.floor(end); i += 1 ) {
arr.push( i );
}
return arr;
}
Even though there's a perfectly good solution from pseudosavant, I'm mentioning this because indirectly referencing elements is a useful trick to have up your sleeve when doing things like two-way binding with tabular data.
Mustache is logic less, and if you do not provide two arrays, you cannot split the entries in two divs.
When the template is rendered each occurring {{key}} is substituted with the same value, for each element in the list.
For this you could probably use handlebars.
You can probably do it by iterating over the list twice in a template and still only using one array. Just use a conditional in each iteration to to the first or second half of the list.
Template has two sections: one for each half
<div id="firstHalf">
{{#each names:i}}
{{#if i < names.length / 2}} // Conditional to only display first half of `names`
{{firstName}} {{lastName}}
{{/if}}
{{/each}}
</div>
<div id="secondHalf">
{{#each names:i}}
{{#if i > names.length / 2}} // Conditional to only display second half of `names`
{{firstName}} {{lastName}}
{{/if}}
{{/each}}
</div>
Inspired by code from here: http://pastie.org/9415897#

Categories