javascript - 如何使用模型将列表从 View 正确连接到 Controller

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

我想要一个表单,允许用户填写许多字段,当 Controller 接收到这些字段时,这些字段应该压缩为一个对象。我很难找到合适的词语来解释这一点,所以让我举个例子。

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

AllocationInformation
{
    FundSource
    Function
    Location
    Program
    Subject
    Object
}

现在模型有 List<AllocationInformation>在里面。该 View 将允许用户创建任意数量的分配对象。

表单中有六个字段,还有一个按钮,用户可以根据需要多次添加相同的六个字段中的另一个字段。

如果这些对象已存在于模型中, View 还需要能够渲染这些对象。我可以完成所有这些工作,但这就像罪恶一样丑陋,我希望找到更好的方法。

目前我的看法是这样的:

<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; }
}

最佳答案

您的 Razor 实现看起来不错。这应该正确回发 List<AllocationInformation>对象(如果它是所有现有对象)。

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

实际上,获取当前索引很简单。在你的addAllocation JavaScript 方法,只需执行:

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

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

就其值(value)而言,您可能需要考虑将 Knockout JS 之类的东西集成到您的项目中。它使得处理诸如渲染集合之类的事情变得微不足道。您可以创建一个通用的“行”模板,然后绑定(bind)该模板以针对某个可观察数组的每个实例进行渲染。然后,要添加或删除行,您只需在数组中添加或删除项目,Knockout 就会相应地更新 HTML。

编辑

值得一提的是,我为您提供的查找当前索引的方法仅在项目未从带外删除的情况下才有效。例如,如果您从三个项目(索引 0、1 和 2)开始,删除第二个项目(索引 1),然后添加一个新项目,则实际上最终会再次获得索引 0、2 和 2 。显然这是行不通的。您可以在删除一项后尝试对所有现有项目重新建立索引,这需要选择每一行,并使用基于 for 循环的新索引更新名称参数。然而,这无疑是非常笨拙的。这是您应该使用 Knockout JS 之类的东西的另一个原因。所有这些逻辑都是开箱即用的。

更新:淘汰赛速成类(class)

这就是我通常使用 Knockout 代码为我的 View 设置代码的方式:

外部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;
};

页面内 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对象是 View 的核心功能。你可以随意命名它;它不必被称为 Application .

Namespace.Allocation只是 AllocationInformation 的 JavaScript 对象表示你有课。存储它可以让您轻松地基于此对象模板添加新的分配。

Namespace.ViewModel是主要的淘汰赛 View 模型,并且 Namespace.AllocationViewModel是特定于单个分配的 View 模型。主视图模型最终将得到一个可观察的数组(通过 $.map )。

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

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

    return model;
}

它会自动为 data 的任何成员创建可观察量目的。然后,您只需将所需的其他内容添加到 View 模型中,而不必手动拼出每个属性。

关于javascript - 如何使用模型将列表从 View 正确连接到 Controller ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28284271/

相关文章:

javascript - 无法使用 css 将过渡添加到 img

c# - 如何让 URL 客户端来自反向代理?

c# - 从 UI 线程中的异步组件触发事件

asp.net-mvc - 什么验证码系统与 ASP.NET MVC 兼容?

c# - Web API 中的异常处理并返回 JSON

asp.net-mvc - 用于移动应用程序的 REST API 上的 OAuth

javascript - 无法将上传的图像保存到 Hapi.js 中的数据库

javascript - 无法使用 Puppeteer 从 dolartoday.com 抓取输入值

javascript - Websockets - 在 onmessage() 之外传递数据

c# - 具有多种实现的 .NET Core 3.1 中的依赖注入(inject)