I'm binding an observableArray
to a template that generates a table with a <tr>
for each item in the array. The idea is that, when the user enters text in the last row, the page automatically adds another row; it's a dynamically-expanding list of entries.
This all works fine, but the keyboard focus is lost when this happens. I posted the broken sample in this fiddle, which contains the following:
function ingredientViewModel(name, qty, note) {
this.name = ko.observable(name);
this.quantity = qty;
this.note = note;
}
var viewModel = {
ingredients: ko.observableArray([]),
};
// When last ingredient's name changes,
// Add a new row to the list
// Update the global subscription to point to the new item
function lastIngredientNameChanged(newValue) {
var currentfocus = document.activeElement;
if (newValue != '') {
lastIngredientSubscription.dispose();
viewModel.ingredients.push(new ingredientViewModel('', '', ''));
lastIngredientSubscription = viewModel.ingredients()[viewModel.ingredients().length - 1].name.subscribe(lastIngredientNameChanged);
}
currentfocus.focus();
}
// Set up initial entries
viewModel.ingredients.push(new ingredientViewModel('', '', ''));
var lastIngredientSubscription = viewModel.ingredients()[viewModel.ingredients().length - 1].name.subscribe(lastIngredientNameChanged);
ko.applyBindings(viewModel);
And this View code:
<script type="text/html" id="ingredientTemplate">
< table id = "ingredienttable" > < colgroup > < col width = "200" / > < col width = "40" / > < col / > < /colgroup>
<thead><tr>
<td>Name</td > < td > Amount < /td>
<td>Note</td > < /tr></thead > < tbody > {
{
each ingredients
}
} < tr class = "ingrediententry" > < td > < input class = "ingredientautocomplete"
data - bind = "value: name, valueUpdate: 'afterkeydown'" / > < /td>
开发者_开发知识库 <td><input data-bind="value: quantity" / > < /td>
<td><input data-bind="value: note" / > < /td>
</tr > {
{
/each}}
</tbody > < /table>
</script>
<div data-bind="template: 'ingredientTemplate'"></div>
Any ideas?
The issue is that when you use {{each}} and the observableArray that you are looping on changes, then the entire template is re-rendered. So, your currentfocus
element is actually gone.
What you can do is switch to using the template binding's foreach
option, which will only re-render changed rows in your template. Your HTML would look like:
<script type="text/html" id="ingredientTemplate">
<table id="ingredienttable">
<colgroup>
<col width="200"/>
<col width="40"/>
<col/>
</colgroup>
<thead><tr>
<td>Name</td>
<td>Amount</td>
<td>Note</td>
</tr></thead>
<tbody data-bind="template: { name: 'rowTmpl', foreach: ingredients }">
</tbody>
</table>
</script>
<script id="rowTmpl" type="text/html">
<tr class="ingrediententry">
<td><input class="ingredientautocomplete" data-bind="value: name, valueUpdate: 'afterkeydown'" /></td>
<td><input data-bind="value: quantity" /></td>
<td><input data-bind="value: note" /></td>
</tr>
</script>
<div data-bind="template: 'ingredientTemplate'"></div>
Sample here: http://jsfiddle.net/rniemeyer/T9UP6/
If you do it this way, then you don't even have to track the currentfocus, it will just remain.
精彩评论