how to use two different observable arrays knockout - javascript

What i am trying to do is, from my ajax call get the first result and put it into the .portfolio--active div and then remove this first item from the ajax results then loop through the rest of the items in the .portfolio--active.
The looping is working perfectly. The problem I am having is with the .portfolio--active. I just don't understand how i am meant to output data without it being in a loop or someway of referencing the function name. for example: <ul data-bind="foreach: items"> reefers to this: hutber.portfolio.ko.self.items = ko.observableArray([]); without it being in a l
Markup
<section>
<h2>portfolio</h2>
<div class="portfolio--active">
<!--<img alt="" src="/img/clients/vw.jpg">-->
<img alt="" data-bind="attr: {src: '/img/clients/' + headline.id+'.jpg'}">
<h3>Volkswagen.co.uk</h3>
<date>Febuary, 2012 - Zone Ltd.</date>
<p>Lorem text</p>
<tags><i title="jQuery" class="icon-rss upsideLeft"></i><i title="jQuery" class="icon-html5 upsideLeft"></i></tags>
</div>
<div class="portfolio--options">
<ul data-bind="foreach: items">
<li data-bind="attr: {'data-id': $data.id}">
<img alt="" data-bind="attr: {src: '/img/clients/' + $data.id+'.jpg'}">
<h4 data-bind="text: title"></h4>
</li>
</ul>
</div>
</section>
JS
hutber.portfolio.ko = {
init: function(){
ko.applyBindings(new hutber.portfolio.ko.portfolioViewModel());
},
portfolioViewModel: function(){
hutber.portfolio.ko.self = this;
hutber.portfolio.ko.self.items = ko.observableArray([]);
hutber.portfolio.ko.self.headline = ko.observableArray([]);
$.getJSON('/portfolio/json').done(function(info){
//build headline item
hutber.portfolio.ko.self.headline(info.slice(0,1));
//remove first item in array only leave none headline items
info = info.slice(1,info.length);
//update items with updated info
hutber.portfolio.ko.self.items(info)
});
}
};

You can reference the [0] index of the array in your bindings, but in your case it seems like maybe you should make headline just an observable and just do an info.shift() to remove and return the first item in the array to set the value of headline. Then, you can just set items as info without doing any slicing.
$.getJSON('/portfolio/json').done(function(info){
//build headline item
hutber.portfolio.ko.self.headline(info.shift());
//update items with updated info
hutber.portfolio.ko.self.items(info)
});

Related

How iterate a jQuery function for each slide in a slider?

Summary: The purpose of this is to display a 3-part slider that users can click through, and they can use the index on each slide to filter the cards on that slide to a specific topic. The filters are working fine, but there's a problem in the JavaScript when it populates the indices: it's populating an index for all 3 cards, and showing that large index on each slide. Instead, the index for each slide needs to be unique, and only contain the hashtags from the cards in that same slide. I really want to avoid duplicating code for the different slides.
HTML
The following HTML has 3 (li) slides. Each slide contains a visible index (.hashtag-list), and one or more article cards (.item). Each (.item) besides the first one contains a hidden input with one or more hashtag values.
<li class="trend-cards">
<div class="card-items">
<div class="item trendingtopiccardblock">
<div class="hashtag-list"></div>
</div>
<div class="item">
<input class="tags" type="hidden" value="TopicA,TopicB"/>
</div>
<div class="item">
<input class="tags" type="hidden" value="TopicC"/>
</div>
</div>
</li>
<li class="trend-cards">
<div class="card-items">
<div class="item trendingtopiccardblock">
<div class="hashtag-list"></div>
</div>
<div class="item">
<input class="tags" type="hidden" value="TopicC, TopicD"/>
</div>
<div class="item">
<input class="tags" type="hidden" value="TopicA,TopicC,TopicD"/>
</div>
</div>
</li>
<li class="trend-cards">
<div class="card-items">
<div class="item trendingtopiccardblock">
<div class="hashtag-list"></div>
</div>
<div class="item">
<input class="tags" type="hidden" value="TopicA, TopicD"/>
</div>
<div class="item">
<input class="tags" type="hidden" value="TopicB,TopicC,TopicD"/>
</div>
</div>
</li>
JavaScript
The following jQuery pulls the values from the .tags classes, stores them in an array, removes duplicates, sorts them, and then populates the HTML in a callback. (Ignore the countryButtons and countries array, as that's not relevant.)
populateHashtagList: function() {
var $cards = $(".card-items .tags");
var list = [];
var $countryButtons = $('.card-filtering li .country-filtering');
var countries = [];
$countryButtons.each(function() {
countries.push(this.firstChild.data.replace("#", "").toLowerCase());
});
//Get tag values, set to lowercase and store in List array
$cards.each(function() {
var tags = getTags($(this).val());
$(tags).each(function (index, value) {
var tagValue = value.toLowerCase();
if($.inArray(tagValue, countries) === -1) list.push(value);
});
});
//Remove duplicates from the array
var uniqueTags = [];
$.each(list, function(i, el){
if($.inArray(el, uniqueTags) === -1) uniqueTags.push(el);
});
uniqueTags.sort();
function getTags(parameter) {
var arr = parameter.split(',');
return arr;
}
//Populate hash-tag List
var hashtagList = $('.hashtag-list');
populateHashtagList();
function populateHashtagList(callback) {
$.each(uniqueTags, function(i, el){
var htmlToInsert = '<span class="active">' + el + '</span>';
hashtagList.append(htmlToInsert);
});
if(typeof callback == "function")
callback();
}
}
What I've tried
Isolating the function using a $(".trend-cards").each function. This resulted in the same large list, but it was tripled on each slide.
Adding more specific paths to the .tags selectors, which changed nothing.
Using a parent selector once the .tags variable is set, and calling the remainder of the function from there. No hashtags populate in this case.
I appreciate any feedback and input on this. I want to learn how to do this better in the future. Thank you very much!
Wrapping this code in .each() function is the best solution here. You said you tried that and you probably forgot to specify parent element for cards and hashtag-list selectors.
Here is a working example: https://jsfiddle.net/k3oajavs/
$(".trend-cards").each(function(){
var $cards = $(".card-items .tags", this);
// ...
var hashtagList = $('.hashtag-list', this);
});

Select multiple items, not just one (angularJs)

I have displayed products in divs and users can click on them, to select them. The problem i have is that only one product can be selected. I wan't to change this functionality to be multi selected and function should collect id's of products.
This is my HTML for products
<div class="product_item hit w_xs_full" ng-repeat="p in products track by $index" style="float: left; background-color: blue;">
<figure class="r_corners photoframe type_2 t_align_c tr_all_hover shadow relative"
ng-click="selectUserItems($index)" ng-class="{sel: $index == selected}">
<!--product preview-->
<img ng-src="images/products/{{p.images[0].thumbName}}" alt="{{p.name}}" class="tr_all_hover">
<!--description and price of product-->
<figcaption>
<br>
<h5 class="m_bottom_10"><b>{{p.name}}</b></h5>
<p class="scheme_color f_size_large m_bottom_15" style="color:black;">Located in: <b>{{p.country}}</b></p>
<a href="#/swap/{{p.mainCategorieLink}}/{{p.subCategoryLink}}/{{p.alias}}">
<button class="button_type_4 bg_scheme_color r_corners tr_all_hover color_light mw_0 m_bottom_15">See item</button>
</a>
</figcaption>
</figure>
</div>
And this is my controller function for selecting items
//select items
$scope.selectUserItems = function(index){
$scope.selected = index;
};
Once item is selected background of div is colored blue
.sel {
background-color:#5ACBFF;
}
So how to properly write controller function so you can select multiple divs and $scope.selectd variable collects ids of products.
Currently, $scope.selected = index; holds only one index, you either need to make it an array and toggle the current selection:
Variant 1
$scope.selected[index] = !$scope.selected[index];
and change your ngClass definition:
ng-class="{sel: $index[selected]}"
Variant 2
or change your ngClick-function:
ng-click="selectUserItems(p)"
And the selectUserItems function
$scope.selectUserItems = function(item){
item.selected = !item.selected;
};
and change your ngClass definition:
ng-class="{sel: p.selected}"
In the product object, add a field 'selected' to control whether the item is selected. And toggle the value of selected in ng-click:
<figure class="...." ng-init="p.isSelected = false"
ng-click="p.isSelected = !p.isSelected" ng-class="{sel: p.isSelected}">

How to get data value of a div that has no ID?

Below is part of some code on an html page that lists shopping cart products. Using JavaScript/jQuery, I need to be able to loop through the li items and get the div "data-" values for each. The issue that I am having is that there are no IDs for the div that has the data- value (). I only see the div for "CategoryContent".
<div class="Block CategoryContent Moveable Panel" id="CategoryContent">
<ul class="ProductList ">
<li class="Odd">
<div class="ProductImage QuickView" data-product="577">
<img src="http://cdn3.example.com/products/577/images/1731/2311-.jpg?c=2" alt="Sweater Vest V-Neck Cotton" />
</div>
<div class="ProductDetails">
Sweater Vest V-Neck Cotton
</div>
<em class="p-price">$45.04</em>
<div class="ProductPriceRating">
<span class="Rating Rating0">
<img src="http://cdn3.example.com/templates/custom/images/IcoRating0.png?t=" alt="" style="" />
</span>
</div>
<div class="ProductActionAdd" style="display:;">
Choose Options
</div>
</li>
</ul>
</form>
</div>
So, there is only one li item here, on a typical page, there are up to 9. My goal is to use JavaScript to get the data-product values and then use that to look up a better image thumbnail and have it replaced. So, how do I get the value set for data-product?
Quite easy:
// Loop through all list items of ul.ProductList:
$("ul.ProductList li").each(function (index, element) {
// Find the element with attribute data-product:
$dp = $(element).find("[data-product]");
// Get the value of attribute data-product:
var product = $dp.attr("data-product");
// Now set the high quality thumbnail url:
var url = "/images/hq/" + product + ".png"; // Note that this is just an example
// Here you can use $(element) to access to current li (and the img):
$(element).find('.ProductImage img').attr('src', url);
});
You can use this:
$("#CategoryContent div[data-product]").each(function(){
alert($(this).attr('data-product'));
});
Pure JS:
var divs = document.querySelectorAll('#CategoryContent div[data-product]');
var index = 0, length = divs.length, prodIds = [];
for ( ; index < length; index++) {
prodIds.push(divs[index].getAttribute('data-product'));
}
Fiddle: http://jsfiddle.net/we5q7omg/
You could use the class name to get the data-product
var products = document.getElementsByClassName("ProductImage");
for(var i=0; i<products.length;i++)
{
console.log(products[i].getAttribute("data-product"));
}
Using JQuery, you can get the elements with the data-product attribute by simply calling
$('[data-product]')
// Or if you only want the data-product elements within the UL.
$('ul').find('[data-product]')
From there you can simply do pull the products from the elements. For Example:
var products = $('[data-product]').map(function() {
return $(this).data('product');
});

ng-repeat: showing an item on click and hiding the others

I have a ng-repeat which display a list of divs and when I click on one it shows an aditionnal div for the clicked item.
This is working
<div ng-repeat="item in items">
<div ng-click="showfull = !showfull">
<div>
<h1>{{item.title}}</h1>
<p>{{item.content}}</p>
</div>
</div>
<div ng-show="showfull">
<p>{{item.info}}</p>
</div>
<hr/>
</div>
My items are loaded from a json containing a list of item and each item have a default attribut showfull set to false in this json. This is working, but now I want to hide all others item in the list when an item is clicked. I've tryied something like this :
This is not working
<div ng-repeat="item in items">
<div ng-click="expand(item)">
<div>
<h1>{{item.title}}</h1>
<p>{{item.content}}</p>
</div>
</div>
<div ng-show="showfull">
<p>{{item.info}}</p>
</div>
<hr/>
</div>
and in the controller I've added a function :
$scope.expand = function(e) {
e.showfull = !e.showfull;
//TODO: put here a foreach logic to hide all other items
}
But even without the foreach logic this is not working, the item don't show the additionnal div when clicked. I have two question :
I suppose this is due to item being "passed by copy" in the expand function or some kind of scope isolation issue but can you explain me why in detail ?
SOLVED
How can I hide all the additional div of other items when I click an item and show the additionnal content for this item ? Do I need to do a directive ?
NOT SOLVED
Thanks
I think you're on the right track. You need to set the showfull on the item and then use ng-show or ng-if to hide it, or as Gavin mentioned, use a class.
On ng-click you can call your expand function, where you pass the item, toggle it and set all others to hidden.
Template:
<div ng-repeat="item in items>
<div ng-click="expand(item)">
<div>
<h1>{{item.title}}</h1>
<p>{{item.content}}</p>
</div>
</div>
<div ng-show="item.showfull">
<p>{{item.info}}</p>
</div>
<hr/>
</div>
Controller:
$scope.expand = function (item) {
angular.forEach($scope.items, function (currentItem) {
currentItem.showfull = currentItem === item && !currentItem.showfull;
});
};
Your expand method is modifying the item, so your ng-show needs to reference the item:
<div ng-show="item.showfull">
<p>{{item.info}}</p>
</div>
To hide all of your items you need to do something like
$scope.expand = function(item) {
angular.forEach(items, function(i) {
if (i === item) {
i.showfull = !i.showfull;
} else {
i.showfull = false;
}
});
};
Shouldn't the second div you want to show be referencing the item?
<div ng-repeat="item in items>
<div ng-click="expand(item)">
<div>
<h1>{{item.title}}</h1>
<p>{{item.content}}</p>
</div>
</div>
<!-- Added item to ng-show -->
<div ng-show="item.showfull">
<p>{{item.info}}</p>
</div>
<hr/>
</div>

Get specific data from page and write it to div

I'm building an online store with javascript shopping cart. However, the script doesn't allow printing only one or two values when displaying cart, but I need to do this.
Here's what the cart looks like:
<div class="simpleCart_items">
<div>
<div class="headerRow">
<div class="item-name">Tuote</div>
<div class="item-price">Hinta</div>
<div class="item-decrement">-</div>
<div class="item-quantity">Määrä</div>
<div class="item-increment">+</div>
<div class="item-total">Yhteensä</div>
<div class="item-remove">Poista</div>
</div>
<div class="itemRow row-0 odd" id="cartItem_SCI-1">
<div class="item-name">Teipit</div>
<div class="item-price">€0.00</div>
<div class="item-decrement"><img src="css/minus.png" alt="minus">
</div>
<div class="item-quantity">3</div>
<div class="item-increment"><img src="css/plus.png" alt="plus">
</div>
<div class="item-total">€0.00</div>
<div class="item-remove"><img src="css/remove.png" alt="Remove">
</div>
</div>
<div class="itemRow row-1 even" id="cartItem_SCI-3">
<div class="item-name">Car Speaker -hajuste</div>
<div class="item-price">€4.00</div>
<div class="item-decrement"><img src="css/minus.png" alt="minus">
</div>
<div class="item-quantity">1</div>
<div class="item-increment"><img src="css/plus.png" alt="plus">
</div>
<div class="item-total">€4.00</div>
<div class="item-remove"><img src="css/remove.png" alt="Remove">
</div>
</div>
<div class="itemRow row-2 odd" id="cartItem_SCI-5">
<div class="item-name">Teipit (Musta hiilikuitu)</div>
<div class="item-price">€0.00</div>
<div class="item-decrement"><img src="css/minus.png" alt="minus">
</div>
<div class="item-quantity">1</div>
<div class="item-increment"><img src="css/plus.png" alt="plus">
</div>
<div class="item-total">€0.00</div>
<div class="item-remove"><img src="css/remove.png" alt="Remove">
</div>
</div>
</div>
</div>
NOTE: The cart is written via javascript so it isn't visible in page source, only in inspect mode of the browser.
So how would I gather the item-name, item-priceand item-quantity?
I've tried this:
var name = $('.item-name');
var price = $('.item-price');
var quantity = $('.item-quantity');
var data = name + price + quantity;
$('#items').html(data);
But this won't actually do anything.
When doing this -> $('.item-name');
You are just capturing the element as object but not the value.
Now that you got your element as object, you need to extract the value and, in this case, your element object is a div so you can try .text() or .html() (to get the text or html inside the div).
(For this situation I will use text() cause you are working just with values and there is nothing related to html)
Try this:
var name = $('.item-name');
var price = $('.item-price');
var quantity = $('.item-quantity');
var data = name.text() + price.text() + quantity.text();
$('#items').html(data);
Better solution:
This will make clickable the div in which you have the product and match the cartItem_SCI pattern.
So, when user clicks any of the elements of your cart, you will get the name, price and quantity values that will be attached to the $('#items') div using append() method instead of html() (because using this will replace the product information each time the user clicks a div)
$(document).ready(function() {
$('div[id^="cartItem_SCI-"]').css({ cursor:'pointer' });
$('div[id^="cartItem_SCI-"]').click(function() {
var name = $(this).find('.item-name');
var price = $(this).find('.item-price');
var quantity = $(this).find('.item-quantity');
var data = name.text() + ' - ' + price.text() + ' - ' + quantity.text();
$('#items').append(data + '<br/>');
});
});​
You are just getting a reference to the class, add .html() to get the inner html of the element that the class applied to.
var name = $('.item-name').html();
For one item you can get like this.But since you have multiple items make one object like this .
var item={};
$('.item-name').each(function(){item.name=$(this).html()});
$('.item-price').each(function(){item.price=$(this).html()});
$('.item-quantity').each(function(){item.quantity=$(this).html()});
var data='';
for(var i=0;i<item.length;i++)
{
data+=item[i].name+item[i].price+item[i].quantity;
}
$('#items').html(data);

Categories