开发者

Updating observableArray bound to form table results in lost focus

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜