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]);
});
Related
I want to send a value from html to javascript using javascript variable.
I've created a div from javascript like this:
<body>
<div id="row" class="category-cards">
// creates from js
</div>
</body>
<script>
var d1 = document.getElementById("row");
for (let i=5; i>0; i--) {
d1.insertAdjacentHTML('beforeend',
<div class="card card-small card-category">
<div class="card-del-btn">
<button id="btnDel" onclick="deleteCategory(i)"> <b> × </b> </button>
</div>
<!-- displays the record --!>
</div>
}
function deleteCategory(index){
// takes the index and searches the mysql database for match, and deletes the record
}
</script>
Each iteration of the for loop inserts a card of a record from the database, and I wish to delete that record from the document, as well as the database, when the button is clicked.
Is there a way to associate a unique id or value to each card and send it through the onclick?
I have tried sending the value of i but it is always the last index, which in this case was 0.
There many ways to achieve what you looking for but simplest one would be to use custom html attributes and provide element that triggered event as argument to your callback.
This could be achieved like this
function handleClick(element) {
const elementData = element.getAttribute('data-my-data')
// do some stuff here with element data
}
Using your code:
d1.insertAdjacentHTML('beforeend', `
<div class="card card-small card-category">
<div class="card-del-btn">
<button data-my-data="here goes your data" onclick="handleClick(element)"> <b> × </b> </button>
</div>
</div>
`)
I am having some trouble displaying a clicked items hidden elements.
Find below my clicked events code.
'click .stylishTiles'(event, instance) {
event.preventDefault();
var selectedTile = this._id;
Session.set("selectedTile_id2", selectedTile);
$("#hidden").show(300);
$(".selected").addClass('show');
},
'click .show'(event, instance) {
event.preventDefault();
$("#hidden").hide(300);
$(".stylishTiles").removeClass('show');
}
The Session.set("selectedTile_id2", selectedTile) in the clicked event is passed on to the helper via Session.get('selectedTile_id2').
Find below the aimed helper code:
'selectedTile': function () {
var selectedTileId = this._id;
var selectedTile_id2 = Session.get('selectedTile_id2');
if(selectedTileId == selectedTile_id2){
return "selected"
}
}
And find below the code for the targeted template:
<template name="invoicesV2C">
{{#each pendingInvoicesV2C}}
<div class="well well-sm stylishTiles {{selectedTile}}">
<div id ="invoiceAmount"> KES: {{formatCurrency recipientAmount}} </div>
<div id="hidden"> tel: {{recipientNumber}} <br> purpose: {{invoicePurpose}} </div>
</div>
{{/each}}
</template>
The {{#each pendingInvoicesV2C}} in the template correctly generates several items, while correctly hiding their hidden elements by default due to the CSS code
#hidden{
display: none;
}
The much desired effect is that I when I click on any item, its hidden elements, being: <div id="hidden"> tel: {{recipientNumber}} <br> purpose: {{invoicePurpose}} </div> should show, hence
$("#hidden").show(300);
$(".selected").addClass('show');
And in reverse, whenever I click on an item already showing its elements,again being: <div id="hidden"> tel: {{recipientNumber}} <br> purpose: {{invoicePurpose}} </div> it should be hidden.
Currently, regardless of the item I click on, only the first element in the list of items will show/hide its elements.
Kindly help me understand how I can get a clicked element to show its hidden details?
You have numerous errors along with really twisted logic.
Let me help you to simplify that:
CSS:
We're using class instead of id
.hidden {
display: none;
}
Template:
<template name="invoicesV2C">
{{#each pendingInvoicesV2C}}
<div class="well well-sm stylishTiles">
<div class="invoiceAmount">
KES: {{formatCurrency recipientAmount}}
</div>
<div class="toggled hidden">
tel: {{recipientNumber}}
<br>
purpose: {{invoicePurpose}}
</div>
</div>
{{/each}}
</template>
Template Code:
Template.invoicesV2C.events({
'click .stylishTiles'(event) {
event.preventDefault();
const $e = $(event.target).closest('.stylishTiles');
$e.find('.toggled').toggle(300);
}
});
That's all. You don't even need helper and using Session at all.
Here is tiny fiddle, representing how it works: https://jsfiddle.net/iStyx/ff6hvorz/
I am trying to make a basic recipe form that has another form inside it. The inner form uses a collection. I am trying to input that collection into the larger form. I am not sure how to write the code on that particular part.
Ingredients = new Mongo.Collection('ingredients');
Recipe = new Mongo.Collection('recipe');
'submit .recipe_submit': function(event){
Recipe.insert({
recipeServing:event.target.mealserving.value,
recipeIngredients:event.target.,
recipeDirection:event.target.recipedirections.value
})
}
<template name="addingredients">
<h2>Enter Ingredients</h2>
<form class="form-group">
Food Item</span><input name="ingredientType" type="text">
Quantity</span><input name="ingredientQuantity" type="text">
Amount</span><input name="ingredientAmount" type="text" >
<button type="submit">Add</button>
</form>
<div class="col-md-12">
<ul>
{{#each ingredients}}
<li >
<div>
<div>{{this.foodItem}}</div>
<div>{{this.foodQuantity}}</div>
<div>{{this.foodAmount}}</div>
<div class="delete"></div>
</div>
</li>
{{/each}}
</ul>
</div>
</template>
Normally I would use he name="" from the input, but I don't see how this works in this case. I also don't need it to import the delete button either. Any help would be awesome.
Your event needs to extract each value out properly. Here is an example of how you could do it:
Template.addingingredients.events({
'submit .recipe_submit': function(event, template){
Recipe.insert({
recipeType: template.$("input[name=ingredientType]").val(),
recipeQuantity: template.$("input[name=ingredientQuantity]").val(),
recipeAmount: template.$("input[name=ingredientAmount]").val()
})
}
});
I had to rename the keys in your recipe to match the field names, you don't have a 'serving', 'ingredients' or 'direction' elements on your form. I don't think this is what you're looking for in your question, though.
The key point is ff you want to extract a value from an element you can use something like $("input[name=ingredientQuantity]").val() to get the value for an element like <input name="ingredientQuantity" type="text"/>
You can either reference or embed Ingredients inside Recipes. Since the ingredient information is frequently used with the recipe information, I would recommend embedding the Ingredients, but you will first need to grab the ingredients from the inner form with JQuery, which requires distinguishable selectors:
{{#each ingredients}}
<li class="ingredient">
<div class="item">{{this.foodItem}}</div>
<div class="quantity">{{this.foodQuantity}}</div>
<div class="amount">{{this.foodAmount}}</div>
<div class="delete"></div>
</li>
{{/each}}
When the form is submitted, we create an array of Ingredient objects and then embed them as a property of Recipe:
Template.addingingredients.events({
'submit .recipe_submit': function(event, template){
var ingredients = [];
$("li.ingredient").each(function() {
var selector = this;
ingredients.push({
foodItem: $(selector).find(".item").text(),
foodQuantity: $(selector).find(".quantity").text(),
foodAmount: $(selector).find(".amount").text()
});
});
Recipe.insert({
recipeServing: event.target.mealserving.value,
recipeIngredients: ingredients,
recipeDirection: event.target.recipedirections.value
});
}
});
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?
I have been going over a lot of tutorials on how to filter a list and can't find an example for my simple use-case.
I have several buttons such as
Name
Age
Height
I have var persons = {...} object and I display it like
<div ng-repeat="person in persons">
{{person.name...}}
</div>
How do I create a filter so each time I will click on one of the buttons the list will be filtered ?
I have tried addingng-repeat="person in persons | filter:filterPersons"
and on the script side to write:
$scope.filterPersons(person){
if (person.name == "John")
return person;
}
but this is only one use-case (how can I filter by another name?) - in other words - How do I connect the links to the filter?
You can bind your filter to scope variables as you do with any other thing. So all you need is to set the appropriated filter to the scope when the user click and bind it to the ng-repeat filter param. See:
<div ng-app>
<span ng-click="myFilter = {type: 1}">Type 1</span> |
<span ng-click="myFilter = {type: 2}">Type 2</span> |
<span ng-click="myFilter = null">No filter</span>
<ul ng-controller="Test">
<li ng-repeat="person in persons | filter:myFilter">{{person.name}}</li>
</ul>
</div>
function Test($scope) {
$scope.persons = [{type: 1, name: 'Caio'}, {type:2, name: 'Ary'}, {type:1, name: 'Camila'}];
}
Notice that the myFilter is changed when the user clicks the filter, and that it's bound to the ng-repeat filter. Fiddle here. You could also create a new filter, but this solution is far better.
My response is very similar to Caio's. I just wanted to show how to filter out an existing array.
In my ng-repeat I have a search filter that goes through the words. I wanted tabs to look for a string match. So I added a additional filter
<tr class="unEditableDrinks" ng-repeat="drink in unEditableDrinkList | orderBy:'-date'|limitTo:400|filter:search |filter:myFilter">
<td>[[drink.name]]</td>
I only have the top part of my table but this should show the strategy. The second filter called myFilter is attached to the buttons below.
<div class="btn-group" role="group">
<button type="button" class="btn btn-primary" ng-click="myFilter={name:'soda'}">Soda</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-primary" ng-click="myFilter={name:'energy'}">Energy Drinks</button>
</div>
On each button I am able to add a ng-click that goes through myFilter and searches the td with drink.name. In each ng-click I can set the value of name to search. So every title containing soda or energy can be filtered through.