I have a integer observable, pages
, and I want to loop up to the value of pages in the html eg.
pages = ko.observable(3)
produces
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Is there a binding that is suitable for this?
I have a integer observable, pages
, and I want to loop up to the value of pages in the html eg.
pages = ko.observable(3)
produces
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Is there a binding that is suitable for this?
You can write something like this:
<ul data-bind="foreach: new Array(pages())">
<li data-bind='text: $index()+1'></li>
</ul>
Here is working fiddle: http://jsfiddle.net/L8Uy5/
The answer by Artem Vyshniakov is a great quick way to get this running in the view without changing the view model. However, if you expect to expand the setup quickly, or just dislike having (what is arguably unit-testable logic) new Array(pages())
in your view, here's an alternate solution. One additional benefit might be that you can also encapsulate the $index + 1
bit:
function ViewModel() {
var self = this;
self.pages = ko.observable(3);
self.pageArray = ko.computed(function() {
var list = [];
var length = parseInt(self.pages(), 10); // the <input> makes `pages` a string!
for (var i = 1; i <= length; i++) {
list.push(i);
}
return list;
});
}
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<input data-bind="textInput: pages" type="number">
<ul data-bind="foreach: pageArray">
<li data-bind="text: $data"></li>
</ul>
The pageArray
code is mostly bulky because (a) pages
becomes a string because of the input, which might be better solved by making pages
into a writeable computed, and (b) because I chose to use a for
loop / basic solution for creating the range (upon which you could improve).
Here's a suggested version that improves on both counts:
function ViewModel() {
var self = this,
_pages = ko.observable(3);
self.pages = ko.computed({
read: () => _pages(),
write: newVal => _pages(parseInt(newVal, 10))
});
self.pageArray = ko.computed(() => _.range(1, _pages() + 1));
}
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<input data-bind="textInput: pages" type="number">
<ul data-bind="foreach: pageArray">
<li data-bind="text: $data"></li>
</ul>
At any rate, pageArray
and its contents are now fully unit testable and accessible to other bits of view model logic. (If you don't need that, e.g. you're just scaffolding a view, I'd say go with the other answer).
If you just need a simple for
loop you can do something like this:
<select name="something"
data-bind="foreach: new Array(10)">
<option data-bind="text: $index()+1, value: $index()+1"></option>
</select>
You can specify the number of items by replacing (10)
with any number.
This will produce a simple dropdown containing numbers from 1 to 10.
it does not work on update field
You can write something like this:
Here is working fiddle: http://jsfiddle.net/L8Uy5/
This work better https://jsfiddle.net/L8Uy5/54/
function ViewModel(){
var self = this;
self.pages = ko.observable(3);
self.virtual_elements = ko.computed(function(){
var arr = [];
for (var i=1; i<=self.pages(); i++) {
arr.push('some');
}
return arr;
});
}
ko.applyBindings(new ViewModel());
<input data-bind="value: pages"/>
<ul data-bind="foreach: virtual_elements">
<li data-bind='text: $index()+1'></li>
</ul>