【问题标题】:MVC 5 BeginCollectionItem with Partial CRUD带有部分 CRUD 的 MVC 5 BeginCollectionItem
【发布时间】:2015-04-21 14:16:31
【问题描述】:

我在下面对问题进行了更改,它仍然是相同的,但希望通过模型以及我想要实现的目标以及我遇到的问题更加清晰。

下面显示了两个类,Company 和 Employee,Company 有一个Employees 列表。

这将是一个输入表单,因此一开始就没有数据。

最终,我希望用户能够根据需要向 Company 对象模型中添加任意数量的 Employee 对象并更新 Employee 对象

我是否在使用 BeginCollectionItem 的正确轨道上,以便可以添加/删除任意数量的 Employee 对象? 当我单击“添加”按钮时,它会将其带到另一个页面上的部分视图(使用 AjaxActionLink),但不使用 JavaScript。

更新 删除了 AjaxActionLink 并改用 JavaScript。

索引

@model MvcTest.Models.Company
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Company</h2>
<div>
    @Html.LabelFor(m => m.Name)
    @Html.EditorFor(m => m.Name)
</div>
<fieldset>
    <legend>Employees</legend>
    <div id="new-Employee">
        @foreach (var Employee in Model.Employees)
        {
            Html.RenderPartial("_Employee", Employee);
        }
    </div>
    <div>
        <input type="button" id="addemployee" name="addemployee" value="Add Employee"/>
        <br/>
    </div>
    <br/>
    @section Scripts
    {
        <script type="text/javascript">
            $('#addemployee').on('click', function () {
                $.ajax({
                    async: false,
                    url: '/Company/AddNewEmployee'
                }).success(function (partialView) {
                    $('#new-Employee').append(partialView);
                });
            });
        </script>
    }
</fieldset>
<div>
    <input type="submit" value="Submit" />
</div>

_Employee PartialView

    @model MvcTest.Models.Employee

@using (Html.BeginCollectionItem("Employees"))
{
    <div class="employeeRow">
        @Html.LabelFor(m => m.Name)
        @Html.EditorFor(m => m.Name)

        @Html.LabelFor(m => m.Telephone)
        @Html.EditorFor(m => m.Telephone)

        @Html.LabelFor(m => m.Mobile)
        @Html.EditorFor(m => m.Mobile)

        @Html.LabelFor(m => m.JobTitle)
        @Html.EditorFor(m => m.JobTitle)

        <a href="#" class="deleteRow">Delete</a>
    </div>
}

@section Scripts
{
$("a.deleteRow").live("click", function(){
    $(this).parents("div.employeeRow:first").remove();
return false;
});
}

控制器

public class CompanyController : Controller
    {
        // GET: Company
        public ActionResult Index()
        {
            var newCompany = new Company();
            return View(newCompany);
        }
        public ActionResult AddNewEmployee()
        {
            var employee = new Employee();
            return PartialView("_Employee", employee);
        }
    }

型号

public class Company
    {
        [Key]
        public int Id { get; set; }
        [Display(Name = "Company")]
        public string Name { get; set; }
        public List<Employee> Employees { get; set; }

        //public Company()
        //{
        //    Employees = new List<Employee>
        //    {
        //        new Employee{ Name = "Enter name"}
        //    };
        //}
    }
    public class Employee
    {
        [Key]
        public int Id { get; set; }
        [Display(Name="Employee")]
        public string Name { get; set; }
        public string Telephone { get; set; }
        public string Mobile {get;set;}
        [Display(Name="Job Title")]
        public string JobTitle {get;set;}
    }

【问题讨论】:

  • 我想你想动态添加/删除classb中的项目:github.com/danludwig/BeginCollectionItem
  • 这听起来很适合使用 ajax :)
  • 我目前正在研究 AJAX 与它的使用,以前从未使用过 AJAX,我看过有关 BeginCollectionItem 的帖子但没有完全理解它,动态不是现在的首要任务.
  • @SelrekJohn 我在回答中使用 BeginCollectionItem 添加了一个基本结构。你将不得不清理它
  • 如果您的部分视图显示在另一个页面上,那么您没有包含jquery.unobtrusive-ajax.js 文件(或者您的脚本顺序错误,或者有重复的脚本)。您也有UpdateTargetId = "Employees",但似乎没有带有id="Employees" 的元素(但有一个&lt;div class="new-course"&gt;,看起来像您要渲染部分的位置?)。在任何情况下,摆脱Ajax.ActionLink() 并改用jquery

标签: c# asp.net-mvc razor


【解决方案1】:

您无需使用 BeginCollectionItem 即可实现此目的。从不得不自己研究它并尝试将它用于类似的问题,它似乎是为早期版本的 MVC 的这种性质的问题而创建的。

使用局部视图来显示和更新列表。一个局部视图用于显示和遍历对象列表,另一个用于创建新对象,该对象在回发更新列表时将在局部视图中显示新创建的对象和列表。

我在这里发布了一个类似的问题,应该可以解决您的问题,click here

希望这会有所帮助。

更新 你的删除不起作用的原因是你不能从局部视图调用 JS,把它放在主视图中(@section 脚本)。另外,我认为您对 div 中的 class 和 id 关键字有些困惑,请看下面。

所以你应该有:

局部视图

@model MvcTest.Models.Employee
    @using (Html.BeginCollectionItem("Employees"))
    {
        <div id="employeeRow" class="employeeRow">
            @Html.LabelFor(m => m.Name)
            @Html.EditorFor(m => m.Name)

            @Html.LabelFor(m => m.Telephone)
            @Html.EditorFor(m => m.Telephone)

            @Html.LabelFor(m => m.Mobile)
            @Html.EditorFor(m => m.Mobile)

            @Html.LabelFor(m => m.JobTitle)
            @Html.EditorFor(m => m.JobTitle)

            <a href="#" id="deleteRow" class="deleteRow" onclick="deleteFunction()">Delete</a>
        </div>
    }

主视图

    @model MvcTest.Models.Company
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Company</h2>
<div>
    @Html.LabelFor(m => m.Name)
    @Html.EditorFor(m => m.Name)
</div>
<fieldset>
    <legend>Employees</legend>
    <div id="new-Employee">
        @foreach (var Employee in Model.Employees)
        {
            Html.RenderPartial("_Employee", Employee);
        }
    </div>
    <div>
        <input type="button" id="addemployee" name="addemployee" value="Add Employee"/>
        <br/>
    </div>
    <br/>
    @section Scripts
    {
        <script type="text/javascript">
            $('#addemployee').on('click', function () {
                $.ajax({
                    async: false,
                    url: '/Company/AddNewEmployee'
                }).success(function (partialView) {
                    $('#new-Employee').append(partialView);
                });
            });

            $("#deleteRow").live("click", function () {
                $(this).parents("#employeeRow:first").remove();
                return false;
            });
        </script>
    }
</fieldset>
<div>
    <input type="submit" value="Submit" />
</div>

【讨论】:

  • 感谢您的解决方案。
  • 没问题,很高兴它有帮助。
  • 嗨@PurpleSmurph您提到的解决方案在添加新员工时效果很好,但在主视图的FormSubmit上 - 它不会在表单发布上绑定公司模型中的员工列表,并导致0项员工收藏..你能看看这个吗..
  • @AshishShukla 我已经回复了您对我的另一个问题的评论,这个解决方案绝对有效,因为我在我的项目中多次使用它。如果您可以发布您正在使用的代码,我很乐意研究它。您最好发布一个问题,因为我要到周二才能回复您。
  • @PurpleSmurph,是的,我尝试在主视图或父视图中传递 IList 模型,并且我在 Post 操作中收到相同的内容,那么它工作正常,但如果我在主视图并在 Post 操作中接收相同的 Company 模型,然后 Post 操作中的 Company 模型包含 List 的 0 条记录,即在这种情况下表单数据不绑定到子模型。
【解决方案2】:

假设 ClassA 是您的 ViewModel:

您只需要一个局部视图或一个视图即可同时更新两者:

例如Edit.cshtml

@model ClassA

@Html.BeginForm(){

@Html.TextBoxFor(m => m.name)

for(int i = 0; i< Model.classB.Count; i++){

     @Html.TextBoxFor(m => Model.classB[i].name)
<button type="button"> Add </button>
<button type="button"> Remove </button>
}

<input type="submit"  value = "save"/>

}

注意:在您的部分视图中,您使用的是 foreach 循环,MVC 模型绑定器要求输入字段的格式为:

list[0].prop1
list[0].prop2
list[0].prop3

list[1].prop1
list[1].prop2
list[1].prop3

所以为此我们使用 for 循环

然后在控制器中:

[HttpPost]
public ActionResult Edit(ClassA model){


// HEre you will see model.ClassB list
// you can then save them one by one

foreach(var item in Model.classB){

save
}
return View(model);
}

如果您想从列表中动态添加或删除项目:

创建另一个局部视图classBs

        @model List<classB>
        <div id="lists">
            foreach (var contact in Model)
            {
               @Html.Partial("ClassBRow", contact)
            }
    </div>

<button data-action-url="@Url.Action("ClassBRow","CONTROLLER")" class="btn btn-primary pull-right" id="addItem">Add</button>

<script>
 $("#addItem").click(function () {
            var btn = $(this);
            $.ajax({
                url: btn.data('action-url'),
                success: function (html) {
                    $("#lists").append(html);
                }
            });
            return false;
        });

</script>

创建另一个局部视图:ClassBRow.cshtml:

@model classB

 using (Html.BeginCollectionItem("classB"))
    {
            @Html.HiddenFor(m => m.isDeleted, new { data_is_deleted = "false" })
            @Html.TextBoxFor(m => Model.name, new { @class = "form-control" })

<span class="glyphicon glyphicon-trash" data-action="removeItem" title="remove" style="cursor:pointer"></span>

    }

在您的控制器中:

public ActionResult ClassBRow()
{
    return PartialView(new classB());
}

AND Edit.cshtml 变为:

    @model ClassA

    @Html.BeginForm(){

    @Html.TextBoxFor(m => m.name)

@Html.Partial("classBs", model.classB)

    <input type="submit"  value = "save"/>

    }

【讨论】:

    【解决方案3】:

    将视图视为独立的。如果您的部分视图是完整视图,您将传递给它IEnumerable&lt;ClassB&gt;

    所以渲染:

     @Html.Partial("ClassBView", Model.ClassB)
    

    此外,您不能将foreachEditorFor 一起使用,因为没有足够的信息来根据索引创建名称。请改用普通的 for 循环。然后表达式解析器可以将其转换为 name="name[1]" 等,因为表达式中的索引可用:

    @model IEnumerable<Project.Models.ClassB>
    
    @if(Model != null)
    {
        for (int i = 0; i < Model.Count; i++)
        {
        <tr>
            <td>
                @Html.EditorFor(modelItem => Model[i].Name)
                <input type="button" value="Clear" />
                <input type="submit" value="Create" />
            </td>
        </tr>
        }
    }
    

    您的示例中缺少更多内容(例如,ClearCreate 连接到的内容),因此如果您可以提供其余的控制器代码,我将对其进行扩展。

    【讨论】:

    • 非常感谢您的回答,为您+1,将尝试一下,看起来不错,目前两个按钮没有到期,我在控制器中没有创建太多我'我试图从头开始构建一切。我希望用它们做的是添加和删除行。
    • 使用客户端代码(例如 Ajax 调用)来做这些。
    【解决方案4】:

    您可以使用(EditorTemplates)查看(ClassB)如下:

    1- 在 (Views/Home) 文件夹下创建名为 (EditorTemplates) 的文件夹(假设您的控制器名称为 Home):

    2-在创建的(EditorTemplates)文件夹下,创建一个名为(ClassB)的视图

    并为 (ClassB) 视图添加以下模板:

    @model Project.Models.ClassB
    @if(Model != null)
    {
    
        <tr>
            <td>
                @Html.EditorFor(modelItem => Model.Name)
                <input type="button" value="Clear" />
                <input type="submit" value="Create" />
            </td>
        </tr>
    
    }
    

    和(ClassAView)应该如下:

    @model Project.Models.ClassA
    
    <tr>
    <td>
    @Html.EditorFor(m => m.name)
    </td>
    <td>
    @Html.EditorFor(m => m.classB);
    </td>
    </tr>
    

    编辑器将自动遍历为每个对象呈现视图的对象列表。

    【讨论】:

    • 可爱的把戏。我很好奇它如何在没有索引上下文的情况下解析 EditorFor 中的名称?
    • (EditorTemplates) 下的视图名称应与(@Html.EditorFor(m => m.ClassB)) 中的相同,这样映射才会正确。
    • 我仍然看不到内部@Html.EditorFor(modelItem =&gt; Model.Name) 可以生成除name="Name" 以外的任何东西(而不是name="Name[n]",除非它们在模板的底层做了不同的事情。我会试试这个我自己。感谢分享。
    猜你喜欢
    • 1970-01-01
    • 2017-02-15
    • 2015-11-17
    • 1970-01-01
    • 1970-01-01
    • 2016-10-15
    • 1970-01-01
    • 2014-07-09
    • 1970-01-01
    相关资源
    最近更新 更多