在 .NET MVC 应用程序中处理来自动态子表单的数据输入的最佳方法
Best approach in dealing with data inputs from dynamic sub forms in .NET MVC app
我对 .NET MVC 比较陌生,并且一直在努力使用我正在开发的 Web 应用程序。总而言之,这个应用程序由一个表单组成,主要部分是输入客户的姓名,然后是一个添加child 的按钮。单击此按钮时,将通过局部视图显示一个子表单。当点击子表单上的保存按钮时,我不确定如何处理数据(但子表单将折叠,HTML 侧的手风琴样式),但要点是客户可以添加一个或多个 child 子表单来输入他们的 child(ren) 的姓名和年龄。
处理这些表单上的输入的最佳方式是什么?我和一些同事谈过,他们似乎不确定。有人建议我首先将 'parent' 表单保存到数据库中,然后在每个 sub-form (child) 中,当单击保存按钮时,将它们也发送到数据库中,使用parent Id 来连接它们。其他人建议为此使用 Vue。在 model-binding 或将数据发送回控制器方面,您有什么建议?
首先,表单的视图模型如下所示:
namespace ModelBindingObjects.ViewModel
{
public class ParentVM
{
public string Name { get; set; }
public List<ChildVM> Children { get; set; }
}
public class ChildVM
{
public string Name { get; set; }
public int Age { get; set; }
}
}
对于数据库端,模型如下所示:
public partial class Parent
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
}
public partial class Child
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public int ParentId { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public int Age { get; set; }
视图看起来像这样
@model ModelBindingObjects.ViewModel.ParentVM
<h3>A Sample Form</h3>
@using (Html.BeginForm())
{
<div class="container">
<div class="row">
<div class="form-row">
<div class="form-group col-md-6">
@Html.LabelFor(x => x.Name, new { @class = "required" })
@Html.TextBoxFor(x => x.Name, new { @class = "form-control" })
</div>
<div class="col-md-6">
</div>
</div>
</div>
<div class="row">
<div class="form-group col-md-6">
<div class="addChildren">
<h5 class="topspacing-med btmspacing-med">Your Child(ren) Details</h5>
<div class="displayChildForm">
@*@Html.Partial("_AddChild", new ViewDataDictionary()
{
TemplateInfo = new TemplateInfo()
{ HtmlFieldPrefix = "Test" }
})*@
</div>
<button type="button" class="btn btn-outline-secondary btn-sm btn-block btmspacing-med" id="addChild">Add New Child</button>
</div>
</div>
<div class="form-group col-md-6"></div>
</div>
</div>
}
@section scripts {
<script type="text/javascript">
var hideButtons = function () {
$("#addChild").hide();
};
$("#saveChild").click(function () {
console.log("testing");
$(".panel-body").hide();
});
$("#addChild").click(function () {
$.ajax({
url: '@Url.Action("RenderAddChildForm", "Home")',
dataType: "html",
type: 'GET',
success: function (result) {
$('.displayChildForm').append(result);
hideButtons();
},
error: function (xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
},
});
});
</script>
然后局部视图...
@model ModelBindingObjects.ViewModel.ChildVM
<div class="panel panel-primary">
<div class="panel-heading"><h5>Add A Child</h5></div>
<div class="panel-body">
<div class="form-group row">
<div class="col-md-12">
<input type="hidden" name="products.Index" value="test" />
@*@Html.LabelFor(x => x.Name)
@Html.TextBoxFor(x => x.Name, new { @class = "form-control" })*@
<input type="text" name="[test].Name" value="Delaney" />
</div>
</div>
<div class="form-group row">
<div class="col-md-12">
<input type="text" name="[test].Age" value="12" />
</div>
</div>
<div class="btn-group float-right mt-2" role="group">
<button class="btn btn-primary btn-sm btn-block" type="submit" id="saveChild">Save</button>
</div>
</div>
</div>
最后,老控制器:
namespace ModelBindingObjects.Controllers
{
public class HomeController : Controller
{
private string PartialViewPath(string name)
{
return $"~/Views/Partials/{name}.cshtml";
}
// GET: Home
public ActionResult Index()
{
return View();
}
public ActionResult RenderAddChildForm()
{
return PartialView(PartialViewPath("_AddChild"));
}
}
}
这取决于页面流的复杂程度,您对 Vue 的熟悉程度,以及是否有单独的编辑页面或您希望在同一页面内支持 edit/delete。如果它所做的只是节省 children,那么您可以使用 AJAX 请求轻松做到这一点,而无需引入 Vue。
一种方法是将这两个操作分开,因此用户不能添加 child,除非他们已经添加并保存了 parent 的数据。在parent表单保存成功后,return保存了parentId,并禁用了按钮。对于每个子表单,当用户单击 "save child" 时,发送 {parentId, childVm}.
另一种方法是整个页面只有一个保存按钮,即每个 child 没有自己的保存按钮。 displayChildForm
div 充当输入集合,带有单个 "add child" 按钮,可创建更多 children 部分视图并将它们附加到自身。当用户单击页面的保存按钮时,您发送 {parentVm, childVmCollection[]}。
您可以混合使用这两种方法,因此您最终会得到一个 "save parent" 按钮和 displayChildForm
div 中的一个 "save/edit children" 按钮。单击保存 children 按钮时,您发送 {parentId,childVmCollection[]}。
您可以将模型绑定到模型上的 ICollection 属性
一个老歌但一个金曲:https://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
您可能会发现我的一些库也对这类事情有帮助。
https://github.com/mcintyre321/FormFactory
https://github.com/mcintyre321/AspNetCoreJTokenModelBinder
我对 .NET MVC 比较陌生,并且一直在努力使用我正在开发的 Web 应用程序。总而言之,这个应用程序由一个表单组成,主要部分是输入客户的姓名,然后是一个添加child 的按钮。单击此按钮时,将通过局部视图显示一个子表单。当点击子表单上的保存按钮时,我不确定如何处理数据(但子表单将折叠,HTML 侧的手风琴样式),但要点是客户可以添加一个或多个 child 子表单来输入他们的 child(ren) 的姓名和年龄。
处理这些表单上的输入的最佳方式是什么?我和一些同事谈过,他们似乎不确定。有人建议我首先将 'parent' 表单保存到数据库中,然后在每个 sub-form (child) 中,当单击保存按钮时,将它们也发送到数据库中,使用parent Id 来连接它们。其他人建议为此使用 Vue。在 model-binding 或将数据发送回控制器方面,您有什么建议?
首先,表单的视图模型如下所示:
namespace ModelBindingObjects.ViewModel
{
public class ParentVM
{
public string Name { get; set; }
public List<ChildVM> Children { get; set; }
}
public class ChildVM
{
public string Name { get; set; }
public int Age { get; set; }
}
}
对于数据库端,模型如下所示:
public partial class Parent
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
}
public partial class Child
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public int ParentId { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public int Age { get; set; }
视图看起来像这样
@model ModelBindingObjects.ViewModel.ParentVM
<h3>A Sample Form</h3>
@using (Html.BeginForm())
{
<div class="container">
<div class="row">
<div class="form-row">
<div class="form-group col-md-6">
@Html.LabelFor(x => x.Name, new { @class = "required" })
@Html.TextBoxFor(x => x.Name, new { @class = "form-control" })
</div>
<div class="col-md-6">
</div>
</div>
</div>
<div class="row">
<div class="form-group col-md-6">
<div class="addChildren">
<h5 class="topspacing-med btmspacing-med">Your Child(ren) Details</h5>
<div class="displayChildForm">
@*@Html.Partial("_AddChild", new ViewDataDictionary()
{
TemplateInfo = new TemplateInfo()
{ HtmlFieldPrefix = "Test" }
})*@
</div>
<button type="button" class="btn btn-outline-secondary btn-sm btn-block btmspacing-med" id="addChild">Add New Child</button>
</div>
</div>
<div class="form-group col-md-6"></div>
</div>
</div>
}
@section scripts {
<script type="text/javascript">
var hideButtons = function () {
$("#addChild").hide();
};
$("#saveChild").click(function () {
console.log("testing");
$(".panel-body").hide();
});
$("#addChild").click(function () {
$.ajax({
url: '@Url.Action("RenderAddChildForm", "Home")',
dataType: "html",
type: 'GET',
success: function (result) {
$('.displayChildForm').append(result);
hideButtons();
},
error: function (xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
},
});
});
</script>
然后局部视图...
@model ModelBindingObjects.ViewModel.ChildVM
<div class="panel panel-primary">
<div class="panel-heading"><h5>Add A Child</h5></div>
<div class="panel-body">
<div class="form-group row">
<div class="col-md-12">
<input type="hidden" name="products.Index" value="test" />
@*@Html.LabelFor(x => x.Name)
@Html.TextBoxFor(x => x.Name, new { @class = "form-control" })*@
<input type="text" name="[test].Name" value="Delaney" />
</div>
</div>
<div class="form-group row">
<div class="col-md-12">
<input type="text" name="[test].Age" value="12" />
</div>
</div>
<div class="btn-group float-right mt-2" role="group">
<button class="btn btn-primary btn-sm btn-block" type="submit" id="saveChild">Save</button>
</div>
</div>
</div>
最后,老控制器:
namespace ModelBindingObjects.Controllers
{
public class HomeController : Controller
{
private string PartialViewPath(string name)
{
return $"~/Views/Partials/{name}.cshtml";
}
// GET: Home
public ActionResult Index()
{
return View();
}
public ActionResult RenderAddChildForm()
{
return PartialView(PartialViewPath("_AddChild"));
}
}
}
这取决于页面流的复杂程度,您对 Vue 的熟悉程度,以及是否有单独的编辑页面或您希望在同一页面内支持 edit/delete。如果它所做的只是节省 children,那么您可以使用 AJAX 请求轻松做到这一点,而无需引入 Vue。
一种方法是将这两个操作分开,因此用户不能添加 child,除非他们已经添加并保存了 parent 的数据。在parent表单保存成功后,return保存了parentId,并禁用了按钮。对于每个子表单,当用户单击 "save child" 时,发送 {parentId, childVm}.
另一种方法是整个页面只有一个保存按钮,即每个 child 没有自己的保存按钮。 displayChildForm
div 充当输入集合,带有单个 "add child" 按钮,可创建更多 children 部分视图并将它们附加到自身。当用户单击页面的保存按钮时,您发送 {parentVm, childVmCollection[]}。
您可以混合使用这两种方法,因此您最终会得到一个 "save parent" 按钮和 displayChildForm
div 中的一个 "save/edit children" 按钮。单击保存 children 按钮时,您发送 {parentId,childVmCollection[]}。
您可以将模型绑定到模型上的 ICollection 属性
一个老歌但一个金曲:https://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
您可能会发现我的一些库也对这类事情有帮助。
https://github.com/mcintyre321/FormFactory https://github.com/mcintyre321/AspNetCoreJTokenModelBinder