如何使用模型将视图中的列表正确连接到控制器

时间:2015-02-02 18:26:21

标签: javascript c# asp.net-mvc asp.net-mvc-4

我希望有一个表单,允许用户在控制器收到它们时填写一些应该压缩到对象的字段。我很难找到解释这个的话,所以让我举一个例子吧。

这个对象(AllocationInformation)基本上有六个字段,它们是模型的一部分:

AllocationInformation
{
    FundSource
    Function
    Location
    Program
    Subject
    Object
}

现在模型中有一个List<AllocationInformation>。该视图将允许用户创建任意数量的Allocation对象。

因此表单中有六个字段,以及一个按钮,让用户可以根据需要多次添加另外六个字段。

视图还需要能够渲染这些对象(如果它们已存在于模型中)。我可以做所有这些工作,但它像罪一样丑陋,我希望能找到更好的方法。

目前我的观点是这样的:

<button type="button" class="btn btn-primary" onclick="addAllocation()">Add Allocation</button>
<div class="row">
    <div id="allocationsDiv" class="container">
        <div class="row">
            <div class="col-sm-1" id="fundSourceCol">FundSource</div>
            <div class="col-sm-1" id="functionCol">Function</div>
            <div class="col-sm-1" id="locationCol">Location</div>
            <div class="col-sm-1" id="programCol">Program</div>
            <div class="col-sm-1" id="subjectCol">Subject</div>
            <div class="col-sm-1" id="objectCol">Object</div>
            <div class="col-sm-1" id="removeCol">Remove?</div>
        </div>



        @if (Model.Allocations != null)
        {
            for (int i = 0; i < Model.Allocations.Count; i++)
            {
                <div>
                    @Html.TextBoxFor(m => m.Allocations[i], new { @class = "form-control input-xsmall" })
                </div>
            }
        }
    </div>
</div>

这是addAllocation()的javascript:

function addAllocation()
{
    var $row = $("<div class='row'></div>");

    $("#allocationsDiv").append($row);

    var fundSource = "<div class='col-sm-1'><input type='text' name='Allocation.FundSource' class='funding-source-item form-control' /></div>";

    $row.append(fundSource);

    var func = "<div class='col-sm-1'><input type='text' name='Allocation.Function' class='funding-source-item form-control col-sm-1' /></div>";

    $row.append(func);

    var location = "<div class='col-sm-1'><input type='text' name='Allocation.Location' class='funding-source-item form-control col-sm-1' /></div>";

    $row.append(location);

    var program = "<div class='col-sm-1'><input type='text' name='Allocation.Program' class='funding-source-item form-control col-sm-1' /></div>";

    $row.append(program);

    var subject = "<div class='col-sm-1'><input type='text' name='Allocation.Subject' class='funding-source-item form-control col-sm-1' /></div>";

    $row.append(subject);

    var object = "<div class='col-sm-1'><input type='text' name='Allocation.Object' class='funding-source-item form-control col-sm-1' /></div>";

    $row.append(object);

    var $remove = $("<div class='col-sm-1'><i class='glyphicon glyphicon-remove glyphicon-clickable col-sm-1 funding-source-item'></i></div>");

    $remove.on('click', function () {
        $row.remove()
    });

    $row.append($remove);



}

这就是摩擦进来的地方。addAllocation()函数需要知道已经进行了多少次分配的当前索引,以便正确构建输入名称,因为它们应该在形式Allocation[i].FundSource,这意味着我必须有一个全局的javascript变量或类似的东西来跟踪它,这似乎是一个糟糕的主意。

我只是觉得必须有一种方法可以做得更好,而我却不知道。

以下是实际的AllocationInformation课程,以防它有用:

public class AllocationInformation
{
    [Key]
    public int Id { get; set; }

    [ForeignKey("CheckRequestModel")]
    public int CheckRequestId { get; set; }

    public CheckRequestModel CheckRequestModel { get; set; }

    public string FundSource { get; set; }

    public string Function { get; set; }

    public string Location { get; set; }

    public string Program { get; set; }

    public string Subject { get; set; }

    public string Object { get; set; }

    public float Amount { get; set; }

    public int VendorNumber { get; set; }
}

1 个答案:

答案 0 :(得分:1)

你的Razor实现看起来不错。如果List<AllocationInformation>对象是所有现有对象,那么它应该正确回发。

问题在于您的JavaScript实现。首先,您当前的JavaScript代码甚至不会考虑索引。所有字段名称都采用Allocation.Foo的形式。相反,它们需要采用Allocations[N].Foo的形式(注意复数形式)。

实际上,获取当前索引是微不足道的。在您的addAllocation JavaScript方法中,只需:

var currentIndex = $('#allocationsDiv .row').length - 1;

然后,使用currentIndex变量代替字段名称中的N

对于它的价值,您可能需要考虑将Knockout JS等内容集成到您的项目中。它使渲染集合等处理变得微不足道。您可以创建一个通用&#34;行&#34;模板,然后绑定要为某个可观察数组的每个实例呈现的模板。然后,要添加或删除行,您只需要在数组中添加或删除项目,Knockout会相应地更新HTML。

修改

值得一提的是,我给你的方法是找到当前的索引,只有当项目没有被删除时才有效。例如,如果您从三个项目(索引0,1和2)开始,删除第二个(索引1),然后添加一个新项目,您实际上最终会得到索引0,2和再次2。显然这不起作用。您可以尝试在删除之后重新索引所有现有项目,这将需要选择每一行,并使用基于for循环的新索引更新名称参数。然而,这无疑是非常笨重的。这是为什么你应该使用像Knockout JS这样的东西的另一个原因。所有这些逻辑都是开箱即用的。

更新:淘汰赛速成障碍

这就是我通常使用Knockout代码为我的视图设置代码的方法:

外部JS

var Namespace = Namespace || {};

Namespace.Application = (function () {
    var _init = function (data) {
        var viewModel = Namespace.ViewModel(data);
        ko.applyBindings(viewModel);
        _wireEvents(viewModel);
        return viewModel;
    };

    var _wireEvents = function (viewModel) {
        $('#AddAllocation').on('click', viewModel.AddAllocation);
        $('#Allocations).on('click', '.remove', viewModel.RemoveAllocation);
        // other event handlers here
    };

    return {
        Init: _init
    };
})();

Namespace.ViewModel = function (data) {
    var self = {};

    self.Allocations = ko.observableArray(
        $.map(data.Allocations, Namespace.AllocationViewModel)
    );

    self.AddAllocation = function () {
        self.Allocations.push(new Namespace.AllocationViewModel(Namespace.Allocation));
    };

    self.RemoveAllocation = function () {
        var data = ko.dataFor(this);
        self.Allocations.remove(data);
    };

    return self;
};

Namespace.AllocationViewModel = function (data) {
    var self = {};

    self.Id = ko.observable(data.Id);
    self.CheckRequestId = ko.observable(data.CheckRequestId);
    // etc.

    return self;
};

On-Page JS

<script>
    var Namespace = Namespace || {};

    Namespace.Allocation = @Html.Raw(Json.Encode(YourProject.Namespace.To.AllocationInformation));

    $(document).ready(function () {
        var data = @Html.Raw(Json.Encode(Model));
        Namespace.Application.Init(data);
    });
</script>

此处使用的Namespace变量是为您的应用程序使用命名空间的通用指示。在JavaScript中污染全局命名空间是一种糟糕的形式,因此您只需创建一个对您的组织来说唯一的变量,通常是公司名称,然后将所有变量和函数放在该对象上。

Namespace.Application对象是视图的核心功能。你可以随意命名;它不必被称为Application

Namespace.Allocation只是您拥有的AllocationInformation类的JavaScript对象表示。通过存储,您可以根据此对象模板轻松添加新分配。

Namespace.ViewModel是主要的Knockout视图模型,Namespace.AllocationViewModel是特定于单个分配的视图模型。主视图模型最终会有一个可观察的数组(通过$.map)。

在此示例代码中,我手动创建了所有可观察对象。实际上,Knockout的映射插件可以让您执行以下操作:

Namespace.ViewModel = function (data) {
    var model = ko.mapping.fromJS(data);

    return model;
}

它会自动为data对象的任何成员创建可观察对象。然后,您只需要向视图模型添加所需的其他内容,而不必手动拼出每个属性。