3

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

我们的表单以两种方式出现意外:

  1. 单击删除按钮时,它会从 中删除所有值ko.observablearray,而不仅仅是与单击的内容相对应的值。
  2. 当点击整个表单的“提交”按钮时,它会添加一个新元素,ko.observablearray而不是将表单提交到我们的服务器。

为什么我们会看到这种行为?(我知道这是两个独立的问题,但我不确定它们是否是由相同的潜在问题引起的,这就是我将它们发布在一个问题中的原因。)

这是我们的 Razor 视图:

@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>

这是我们的主要敲除代码 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
        });
    }
};
4

2 回答 2

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。由于明显的原因,我没有测试提交功能;-)

于 2012-10-30T11:07:53.660 回答
0

这就是为什么提交功能在我的表单上不起作用的原因:

在视图中,我有这个 Razor:

<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元素会导致它响应用户按下回车而不是表单末尾的提交按钮。当我将按钮改为input元素时,它开始按预期工作。

<input type="button" value="Add another POS Transaction Code"
    data-bind="click: addPOSTransactionCode" />
于 2012-11-20T21:30:21.113 回答