Knockout绑定无法按预期方式操作可观察数组

时间:2012-10-29 23:57:10

标签: javascript razor knockout.js ko.observablearray

我们有一个使用Razor和Knockout.js显示表单的视图。表单的一部分要求用户输入值列表,我们使用ko.observablearray来跟踪它们。此列表表示为一组文本框,每个值一个,每个框旁边有一个“删除”按钮,所有这些框下面都有一个“添加”按钮。它的工作方式与http://learn.knockoutjs.com/#/?tutorial=collections的演示项目类似。

我们的形式出乎意料地以两种方式表现:

  1. 单击删除按钮时,它会删除ko.observablearray中的所有值,而不仅仅是与所单击的内容对应的值。
  2. 点击整个表单的“提交”按钮后,它会向ko.observablearray添加一个新元素,而不是将表单提交给我们的服务器。
  3. 为什么我们会看到这种行为? (我知道这些是两个独立的问题,但我不确定它们是否是由相同的潜在问题引起的,这就是我在一个问题中发布它们的原因。)

    这是我们的剃刀观点:

    @model OurProject.Models.Input.InputModel
    @{
        ViewBag.Title = "Input";
    }
    
    <h2>Inputs</h2>
    
    <div id="inputForm">
        <!-- snip - lots of input elements to fill in that are bound to KO -->
    
        <div>
            @Html.LabelFor(model => model.POSTransactionCodes)
        </div>
        <div>
            <span class="help-block">Separate values by commas.</span>
        </div>
        <div>
            <ul data-bind="foreach: POSTransactionCodes">
                <li><input data-bind="value: $data" /> <a href="#" data-bind="click: $root.removePOSTransactionCode">Delete</a></li>
            </ul>
            <button data-bind="click: addPOSTransactionCode">Add another POS Transaction Code</button>
    
            @Html.ValidationMessageFor(model => model.POSTransactionCodes, null, new { @class = "help-inline" })
        </div>
        <!-- snip - more input elements -->
    
        <button data-bind="click: save">Submit</button>
    </div>
    
    <script type="text/javascript" src='~/Scripts/jquery-1.8.2.min.js'></script>
    <script type="text/javascript" src='~/Scripts/knockout-2.1.0.js'></script>
    <script type="text/javascript" src='~/Scripts/OP/OP.js'></script>
    <script type="text/javascript" src='~/Scripts/OP/Input/OP.Input.Input.Form.js'></script>
    <script type="text/javascript" src='~/Scripts/OP/Input/OP.Input.Input.Data.js'></script>
    <script type="text/javascript">
        var elementToBindTo = $("#inputForm")[0];
        OP.Input.Input.Form.init(elementToBindTo);
    </script>
    

    这是我们的主要Knockout代码,OP.Input.Input.Form.js:

    extend(OP, 'OP.Input.Input.Form');
    OP.Input.Input.Form = function (jQuery) {
        //The ViewModel for the page
        var ViewModel = function () {
            var self = this;
    
            //Fields
            /* snip - lots of ko.observables() */
            self.POSTransactionCodes = ko.observableArray([]); //is a list of transaction codes
            /* snip - lots of ko.observables() */
    
            //Set up with initial data
            self.initialize = function () {
                var c = function (data, status, response) {
                    if (status === "success") {
                        /* snip - lots of ko.observables() */
                        ko.utils.arrayPushAll(self.POSTransactionCodes, data.POSTransactionCodes);
                        self.POSTransactionCodes.valueHasMutated();
                        /* snip - lots of ko.observables() */
                    } else {
    
                    }
                };
                OP.Input.Input.Data.GetInput(c);
            }
    
            //When saving, submit data to server
            self.save = function (model) {
                var c = function (data, status, response) {
                    if (status === "success") {
                        //After succesfully submitting input data, go to /Input/Submitted
                        //in order to let MVC determine where to send the user next
                        window.location.href = "~/Input/Submitted";
                    } else {
                    }
                };
                OP.Input.Input.Data.SaveInput(model, c);
            }
    
            //Modifying POSTransactionCodes array
            self.removePOSTransactionCode = function (POScode) {
                self.POSTransactionCodes.remove(POScode)
            }
    
            self.addPOSTransactionCode = function () {
                self.POSTransactionCodes.push("");
            }
        };
    
        //Connect KO form to HTML
        return {
            init: function (elToBind) {
                var model = new ViewModel();
                ko.applyBindings(model, elToBind);
                model.initialize();
            }
        };
    } ($);
    

    这是OP.Input.Input.Data.js:

    extend(OP, 'OP.Input.Input.Data');
    OP.Input.Input.Data = {
        GetInput: function (callback) {
            $.get("/API/Input/InputAPI/GetInputModel", callback);
        },
        SaveInput: function (input, callback) {
            $.ajax({
                url: "/API/Input/InputAPI/SaveInput",
                type: "post",
                data: input,
                complete: callback
            });
        }
    };
    

2 个答案:

答案 0 :(得分:2)

您需要将新的ViewModel推送到可观察的数组中。其中包含可观察的属性。

为此,我创建了一个名为TransactionCodeView

的新视图模型
var TransactionCodeView = function() {
  var self = this;
  self.code = ko.observable("");    
};

然后当用户点击“添加另一个POS交易代码”时:

self.addPOSTransactionCode = function () {
    self.POSTransactionCodes.push(new TransactionCodeView());
}

唯一改变的是HTML绑定:

<li><input data-bind="value: code" /> <a href="#" data-bind="click: $root.removePOSTransactionCode">Delete</a></li>

因为code是新视图模型中的可观察属性,我们将input值绑定到该值。

看看这个jsfiddle。我没有测试提交功能,原因很明显; - )

答案 1 :(得分:0)

这就是提交功能无法在我的表单上运行的原因:

在视图中,我有这把剃刀:

<div>
    <ul data-bind="foreach: POSTransactionCodes">
        <li><input data-bind="value: $data" /> <a href="#" data-bind="click: $root.removePOSTransactionCode">Delete</a></li>
    </ul>
    <button data-bind="click: addPOSTransactionCode">Add another POS Transaction Code</button>

    @Html.ValidationMessageFor(model => model.POSTransactionCodes, null, new { @class = "help-inline" })
</div>

使用我的“添加”按钮的button元素导致它响应用户按Enter而不是表单末尾的提交按钮。当我将按钮更改为input元素时,它开始按预期工作。

<input type="button" value="Add another POS Transaction Code"
    data-bind="click: addPOSTransactionCode" />