ASP.NET 包含对象列表的对象的 Core 3.0 MVC CRUD

ASP.NET Core 3.0 MVC CRUD for object that contains list of objects

我有以下 类:

public class Folder
{
     public Folder()
     {
         Documents = new List<Document>()
     }

     public string Name { get; set; }

     public List<Document> Documents { get; set; }
} 

和:

public class Document
{
     public string Name { get; set; }

     public string Path { get; set; }
} 

我需要一个 razor 视图,其中的表单将在提交后保存一个新的 Folder。除了 Name 字段之外,我希望表单还包含 Document 的 table 和一个添加按钮。我希望能够通过按下按钮向 table 添加行(文档)。换句话说,我想动态添加 rows/documents 。按下按钮后,应该会出现一个带有输入字段的新行,供用户填写。用户提交表单后,提交的模型应包含列表中与 table.

行一样多的 Document 对象

您可以在下面找到我到目前为止尝试过的内容。如果有人有更优雅的解决方案,我们非常欢迎!

1) 我尝试搭建局部视图,如下所示,但随后按钮调用了一个新操作。理想情况下,我不想离开屏幕。我希望能够动态添加行,并在我点击提交时将这些 rows/documents 发回我的模型中。生成的文件如下:

@model IEnumerable<Document>

<p>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10 text-right">
            <a asp-action="GetNewDocument" class="btn btn-primary">Add Document</a>
        </div>
    </div>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Path)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Name)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Path)
                </td>
                <td>
                    @Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
                    @Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
                    @Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
                </td>
            </tr>
        }
    </tbody>
</table>

2) 我尝试使用编辑器模板并 Ajax 调用控制器操作以返回新创建文档的部分视图,但是当我提交表单时,文件夹似乎没有文档任何。更具体地说,我有:

AddFolder.cshtml:

@model Folder

<div>
    <h3>Add Folder</h3>
    <input id="add-document" type="button" value="Add Document" class="btn btn-primary" />
</div>
<form asp-controller="Folder" asp-action="AddFolder" method="post" role="form">
    <div class="form-group form-row">
        <label asp-for="Name"></label>
        <input asp-for="Name" type="text" />
    </div>
    div class="form-group form-row">
    <table class="table table-hover" id="documentsTable">
        <thead>
            <tr>
                <th scope="col">
                    DOCUMENT NAME
                </th>
                <th scope="col">
                    DOCUMENT PATH
                </th>
            </tr>
        </thead>
        <tbody>
            @Html.EditorFor(m => m.Documents)
        </tbody>
    </table>
    </div>
</form>

<script type="text/javascript">
    $(document).ready(function () {
        $("#add-document").click(function () {
            $.ajax({
                url: "GetNewDocument",
                success: function (data) {
                    $("#documentsTable").append(data);
                }
            });
        });
    });
</script>

控制器中的 GetNewDocument 操作方法如下所示:

public PartialViewResult GetNewDocument()
{
    return PartialView("NewDocument", new Document());
}

NewDocument部分视图:

@model Document

@Html.EditorForModel()

和 EditorTemplates 文件夹中的Document.cshtml

@model Document
<tr class="document-row">
    @{
        using (Html.BeginCollectionItem("Documents"))
        {
            <td>
                @Html.TextBoxFor(m => m.Name)
                @Html.ValidationMessageFor(m => m.Name)
            </td>
            <td>
                @Html.TextBoxFor(m => m.Path)
                @Html.ValidationMessageFor(m => m.Path)
            </td>
        }
    }
</tr>

在这一点上,我什至不确定这是否是我在 Asp.Net Core 中做我想做的事情的正确方法。 但我很确定我遗漏了一些东西,因为没有 Document 回发并且列表为空。

欢迎任何帮助。 提前谢谢你。

  1. 尝试使用 IList<Document> 作为模型(而不是 IEnumerable<Document>)。
  2. 枚举时,使用标准for each循环:

    @for (var i=0; i < Model.Count; i++)
    {
        var listIdx = i;  // Some hack since there is a caveat with i (can't remember)
        var modelItem = Model[listIdx];
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Path)
            </td>
            <td>
                @Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
                @Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
                @Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
            </td>
        </tr>
    }
    

我找到了解决方法。

AddFolder.cshtml应该有:

<tbody>
@{
    for (int i = 0; i < Model.Documents?.Count(); i++)
    {
        @Html.EditorFor(m => m.Documents[i])
    }
}
</tbody>

而不是:

<tbody>
    @Html.EditorFor(m => m.Documents)
</tbody>

在控制器中,处理提交模型的动作应该有一个额外的参数:

[HttpPost]
public IActionResult AddFolder(Folder model, IEnumerable<Document> Documents)
{
    model.Documents = Documents;

    if (!ModelState.IsValid) {
        return View(model);
    }

    // Persist to db here

    // RedirectToAction or whatever after
}

奖金信息:

如果您需要自定义验证,您将需要每个对象的标识 ID,以便您可以为 AddModelError 方法生成正确的密钥。 检查 post 的答案以了解原因。 为此,您只需包含一个附加参数 IFormCollection form 并获得 id,如下所示:

foreach (var id in form.FirstOrDefault(f => f.Key == "Documents.index").Value) 
{
    // Do what you need to do here
}