我希望有一个表单,允许用户在控制器收到它们时填写一些应该压缩到对象的字段。我很难找到解释这个的话,所以让我举一个例子吧。
这个对象(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; }
}
答案 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
对象的任何成员创建可观察对象。然后,您只需要向视图模型添加所需的其他内容,而不必手动拼出每个属性。