3

我们的 Web 应用程序中有一个表单,它在一个地方要求用户输入一个值列表。我们使用 Razor 和 knockout.js 编写了表单的这一部分,以便列表的每个值都有一个文本框,类似于本教程。我们如何将这些文本框数据绑定到我们的 MVC 模型?

这是我们的表格:

@model OurProject.Models.Input.InputModel
@{
    ViewBag.Title = "Input";
}
<h2>
    Inputs</h2>
<div id="inputKOApp">
    @using (Html.BeginForm())
    {
        <!-- snip - lots of part of our form that work correctly -->
        <div class="row-fluid">
            <div class="control-group">
                <div class="span8 control-label">
                    @Html.LabelFor(model => model.POSTransactionCodes)
                </div>
                <div class="controls">
                    <!-- These are the textboxes we would like to bind to MVC -->
                    <ul class="pull-right" data-bind="foreach: POSTransactionCodes">
                        <li>
                            <input data-bind="value: code" />
                            <a href="#" data-bind="click: $root.removePOSTransactionCode">Delete</a></li>
                    </ul>
                    <button class="pull-right" data-bind="click: addPOSTransactionCode">
                        Add another POS Transaction Code</button>
                    @Html.ValidationMessageFor(model => model.POSTransactionCodes, null, new { @class = "help-inline" })
                </div>
            </div>
        </div>
        <!-- snip - more cshtml that is irrelevant to the problem -->
</div>
        <input type="submit" value="Submit" />
    }
</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 inputApp = $('#inputKOApp')[0];
    OP.Input.Form.init(inputApp);
</script>

这是我们的 knockout.js 脚本 OP.Input.Input.Form.js:

extend(OP, 'OP.Input.Form');
OP.Input.Form = function (jQuery) {
    var TransactionCodeView = function () {
        var self = this;
        self.code = "";
    };

    //The ViewModel for the page
    var ViewModel = function () {
        var self = this;        

        //Fields
        /* snip - lots of fields that work as expected */
        self.POSTransactionCodes = ko.observableArray([]); //is a list of transaction codes

        //Set up with initial data
        /* I'm guessing that we won't need this function anymore since MVC will populate
         * everything for us, but I'll leave it in until I'm far enough along to know
         * I won't need to gut lots of stuff */
        self.initialize = function () {
            var c = function (data, status, response) {
            /* snip - lots of fields that work as expected */
                if (status === "success") {
                    if(data.POSTransactionCodes != null) ko.utils.arrayPushAll(self.POSTransactionCodes, data.POSTransactionCodes);
                    self.POSTransactionCodes.valueHasMutated();
                } else {

                }
            };
            OP.Input.Data.GetInput(c);
        }

        //When saving, submit data to server
        self.save = function (model) {
            var c = function (data, status, response) {
                if (status === "success") {

                } else {
                }
            };
            OP.Input.Data.SaveInput(model, c);
        }

        //Modifying POSTransactionCodes array
        self.removePOSTransactionCode = function (POScode) {
            self.POSTransactionCodes.remove(POScode);
        }

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

    //Connect KO form to HTML
    return {
        init: function (elToBind) {
            var model = new ViewModel();
            ko.applyBindings(model, elToBind);
            model.initialize();
        }
    };
} ($);

这是我们的 MVC 模型:

namespace OurProject.Models.Input
{
    public class InputModel : IModel
    {
        //Snip - lots of properties that aren't interesting for this problem
        [Required]
        [DisplayName("POS Transaction Codes")]
        public List<double> POSTransactionCodes { get; set; }

        public InputModel()
        { }

        /* I ommitted a few other methods that
         * aren't relevant to this problem. */
    }
}
4

2 回答 2

2

我看不到您如何将数据发送回服务器,但您需要以允许模型绑定的方式命名您的输入:

如果您要绑定到列表/集合,您的输入应该是这样的名称:

<input type="text" name="CollectionPropertyName[index]" />

您可以在本文中阅读有关模型绑定到列表的信息

因此,您只需要为您的输入生成正确的名称:

<input data-bind="value: code, attr: { name: 'POSTransactionCodes[' + $index() + ']' }" />

您应该注意,上述解决方案可能仅在您使用提交按钮时才有效,如果您form-urlencoded以 json 格式发送数据,则可能需要调整序列化逻辑以使模型绑定器满意:

在这种情况下,您的 json 应该是这样的:

{
   //...
   POSTransactionCodes: [ 1 , 3  ]
   //..
}
于 2012-11-07T06:11:26.370 回答
1

感谢 nemesv 的回答,让我明白这一点。

但我需要有用于 jquery 验证的“正常”名称和 id 属性。所以我想出了编写自己的数据绑定器的想法。

ko.bindingHandlers.nameId = {
    update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = valueAccessor(),
            allBindings = allBindingsAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(value);
        var bindName = $(element).data('bind-name');
        bindName = bindName.replace('{0}', valueUnwrapped);
        $(element).attr({name : bindName, id : bindName});
    }
};

并在 html 中使用

<input
      data-bind="value: qty, nameId: $index"
      data-bind-name="inventory[{0}].qty" />

jsfiddle

于 2014-04-10T03:23:36.997 回答