ASP. NET Core 3.0 MVC CRUD для объекта, который содержит список объектов - PullRequest
0 голосов
/ 09 января 2020

У меня есть следующие классы:

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

Мне нужно бритвенное представление с формой, которая собирается сохранить новый Folder после отправки. Помимо поля Name, я хочу, чтобы форма также содержала таблицу для Document s и кнопку Добавить. Я хочу иметь возможность добавлять строки (документы) в таблицу, нажав кнопку. Другими словами, я хочу динамически добавлять строки / документы. После нажатия кнопки должен появиться новый ряд с полями ввода, чтобы пользователь мог их заполнить. После того, как пользователь отправит форму, представленная модель должна содержать столько объектов Document в списке, сколько строк таблицы.

Ниже вы можете найти то, что я пробовал до сих пор. Если у кого-то есть более элегантное решение, это более чем приветствуется!

1) Я попытался создать частичное представление, как показано ниже, но затем кнопка вызывает новое действие. В идеале я не хочу покидать экран. Я хочу иметь возможность динамически добавлять строки, и эти строки / документы будут публиковаться внутри моей модели при нажатии кнопки Отправить. Сгенерированный файл выглядит следующим образом:

@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.cs html:

@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()

и Document.cs html в папке EditorTemplates:

@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 s не отправлено обратно, а список пуст.

Любая помощь приветствуется. Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 09 января 2020

Я нашел обходной путь.

AddFolder.cs html должен иметь:

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

Информация о бонусе:

Если вам нужны пользовательские проверки, вам потребуется идентификационный идентификатор каждого объекта, так что что вы можете создать правильный ключ для метода AddModelError. Проверьте ответ в этом сообщении, чтобы понять, почему. Для этого вы просто включаете дополнительный параметр IFormCollection form и получаете id s следующим образом:

foreach (var id in form.FirstOrDefault(f => f.Key == "Documents.index").Value) 
{
    // Do what you need to do here
}
0 голосов
/ 09 января 2020
  1. Попробуйте использовать IList<Document> в качестве модели (вместо IEnumerable<Document>).
  2. При перечислении используйте starndard for each l oop:

    @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>
    }
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...