Bootstrap carousel with Knockout content
Trying to make a Boostrp 3 Carousel with a dynamic amount of slides, dependant on variable content
HTML
<script src="http://knockoutjs.com/downloads/knockout-2.3.0.js"></script>
<script>
$('.carousel').carousel({
})
</script>
<div class="row">
<div id="carousel-home" class="carousel slide" data-interval="false" data-wrap="false">
<!-- Wrapper for slides -->
<div class="carousel-inner">
<p> I need to get the code within the ko foreach comments below to repeat to match the number of pages in 'pageCount' but it will not because of the data bindings. I can't work out what I need to do or add in the view model. I also need to just make the first slide 'active', not every slide. </p>
<!-- ko foreach: new Array(pageCount()) -->
<div class="item active">
<div class="feature-item-container" data-bind="foreach: features">
<span data-bind="badge: badgeUrl, popover: { trigger: 'hover', html: true, placement: 'left auto' }">
<a data-placement='left' data-bind="attr: { href: target }">
<img class="feature-item" name="feature_item" data-bind="attr: { src: icon, alt: name }">
</a>
</span>
</div>
</div>
<!-- /ko -->
<!-- ko foreach: new Array(pageCount()) -->
<div class="item">
<P>This item repeats to match the number of pages in 'pageCount'
but the content above doesn't. I can't work out why after googling all week and looking through SO questions.</p>
</div>
<!-- /ko -->
</div>
<!-- Controls -->
<a class="left carousel-control" href="#carousel-home" data-slide="prev" rel="nofollow"><i class="fa fa-chevron-left fa-3x" data-bind="click: previousPage, style: { color: page() > 1 ? '#999' : '#DFDFDF' }"></i></a>
<a class="right carousel-control" href="#carousel-home" data-slide="next" rel="nofollow"><i class="fa fa-chevron-right fa-3x" data-bind="click: nextPage, style: { color: page() < pageCount() ? '#999'...
CSS
.carousel-control {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 6%;
font-size: inherit;
text-align: center;
text-shadow: none;
opacity: 1;
filter: none;
}
.carousel-control.left {
background-image: none;
filter: none;
}
.carousel-control.right {
right: 0;
left: auto;
background-image: none;
filter: none;
}
.carousel-indicators {
bottom: 10px;
}
.carousel-indicators li {
border-radius: 0;
background-color: #c7c7c7;
border:1px solid #999;
width:12px;
height:12px;
}
.carousel-indicators .active {
margin:1px;
background-color: #fff;
}
JavaScript
ko.bindingHandlers.popover = {
update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var model = ko.unwrap(valueAccessor());
model.title = viewModel.name;
model.content = viewModel.hintText;
$(element).popover(model);
}
};
ko.bindingHandlers.badge = {
update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var badge_uri = ko.unwrap(valueAccessor());
if( badge_uri > '' ) {
$.get(badge_uri, function(data) {
var badge = '<span class="icon-badge label label-danger">' + data.value + '</span>';
$(element).prepend(badge);
});
}
}
};
function FeaturesViewModel() {
var self = this;
self.limit = 2;
self.page = ko.observable(1);
self.pageCount = ko.observable(1);
self.features = ko.observableArray();
self.refresh = function() {
$.get('/api/features?page=' + self.page() + '&limit=' + self.limit, function(data) {
self.features(data.features);
self.pageCount(data.pageCount);
});
};
self.previousPage = function() {
self.page( self.page() - 1 );
if( self.page() < 1 ) self.page( 1 );
self.refresh();
};
self.nextPage = function() {
self.page( self.page() + 1 );
if( self.page() > self.pageCount() ) self.page( self.pageCount() );
self.refresh();
};
}
$(function() {
var featuresModel = new FeaturesViewModel();
ko.applyBindings(featuresModel);
featuresModel.refresh();
});