Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
206 views
in Technique[技术] by (71.8m points)

c# - MVC 5 Dynamic Rows with BeginCollectionItem

What is the best way to add/delete rows to a table when a button is clicked? I need rows created from ChildClass properties (child class is a list within the main class/model).

Currently have a View (model is MyMain) which references a Partial View using RenderPartial.

The partial view displays the properties of the model, a class called MyChild which is an object list within MyMain.

I want to have add and delete buttons to dynamically add the rows which are held within the partial view.

So adding MyChild repeatedly for more rows on the list. Is this possible? Or should I not be using partial views for this?

Updated Code

Below are the current classes and views I'm working with, I've been trying to implement the BeginCollectionItem helper but I'm getting null ref where I'm trying to load the partial view despite the if statement saying to create a new instance of the child class if doesn't exist - why is this being ignored?

Main View

    @using (Html.BeginForm())
{
    <table>
        <tr>
            <th>MyMain First</th>
            <th>MyChild First</th>
        </tr>
        <tr>
            <td>
                @Html.EditorFor(m => m.First)
            </td>
            <td>
                @if (Model.child != null)
                {
                    for (int i = 0; i < Model.child.Count; i++)
                    {
                        Html.RenderPartial("MyChildView");
                    }
                }        
                else
                {
                    Html.RenderPartial("MyChildView", new MvcTest.Models.MyChild());
                }       
            </td>
        </tr>
        @Html.ActionLink("Add another", "Add", null, new { id = "addItem" })
    </table>
}

Partial View

@model MvcTest.Models.MyChild

@using (Html.BeginCollectionItem("myChildren"))
{
    Html.EditorFor(m => m.Second);
}

Models

public class MyMain
{
    [Key]
    public int Id { get; set; }
    public string First { get; set; }
    public List<MyChild> child { get; set; }
}

public class MyChild
{
    [Key]
    public int Id { get; set; }
    public string Second { get; set; }
}

Controller

public class MyMainsController : Controller
{
    // GET: MyMains
    public ActionResult MyMainView()
    {
        return View();
    }

    [HttpPost]
    public ActionResult MyMainView(IEnumerable<MyChild> myChildren)
    {
        return View("MyMainView", myChildren);
    }

    public ViewResult Add()
    {
        return View("MyChildView", new MyChild());
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Updated Answer - The original code is NOT true to 'dynamic', however it allows for everything I needed to do within the question parameters.

Initially, I couldn't get Stephen's BCI suggestion in the question comments working, since then I have and it's brilliant. The code below in the updated section will work if you copy + paste, but you will need to either manually download BCI from GIT or use PM> Install-Package BeginCollectionItem with Package Manager Console in Visual Studio.

I had some issue with various points using BCI due to the complexity and not having done MVC before - here is more information on dealing with accessing class.property(type class).property(type class).property.

Original answer - I've gone with a more clear example below than in my question which quickly got too confusing.

Using two partial views, one for the list of employees and another for the creation of a new employee all contained within the viewmodel of companyemployee which contains an object of company and a list of employee objects. This way multiple employees can be added, edited or deleted from the list.

Hopefully this answer will help anyone looking for something similar, this should provide enough code to get it working and at the very least push you in the right direction.

I've left out my context and initialiser classes as they're only true to code first, if needed I can add them.

Thanks to all who helped.

Models - CompanyEmployee being the view model

public class Company
{
    [Key]
    public int id { get; set; }
    [Required]
    public string name { get; set; }
}

public class Employee
{
    [Key]
    public int id { get; set; }
    [Required]
    public string name { get; set; }
    [Required]
    public string jobtitle { get; set; }
    [Required]
    public string number { get; set; }
    [Required]
    public string address { get; set; }
}

public class CompanyEmployee
{
    public Company company { get; set; }
    public List<Employee> employees { get; set; }
}

Index

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

<h2>Index</h2>
<fieldset>
    <legend>Company</legend>
    <table class="table">
        <tr>
            <th>@Html.LabelFor(m => m.company.name)</th>
        </tr>
        <tr>
            <td>@Html.EditorFor(m => m.company.name)</td>
        </tr>
    </table>
</fieldset>
<fieldset>
    <legend>Employees</legend>

        @{Html.RenderPartial("_employeeList", Model.employees);}

</fieldset>
<fieldset>
    @{Html.RenderPartial("_employee", new MMV.Models.Employee());}
</fieldset>
<div class="form-group">
    <div class="col-md-offset-2 col-md-10">
        <input type="submit" value="Submit" class="btn btn-default" />
    </div>
</div>

PartialView of List of Employees

@model IEnumerable<MMV.Models.Employee>
@using (Html.BeginForm("Employees"))
{
    <table class="table">
        <tr>
            <th>
                Name
            </th>
            <th>
                Job Title
            </th>
            <th>
                Number
            </th>
            <th>
                Address
            </th>
            <th></th>
        </tr>
        @foreach (var emp in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => emp.name)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => emp.jobtitle)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => emp.number)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => emp.address)
                </td>
                <td>
                    <input type="submit" formaction="/Employees/Edit/@emp.id" value="Edit"/>
                    <input type="submit"formaction="/Employees/Delete/@emp.id" value="Remove"/>
                </td>
            </tr>
        }
    </table>
}

Partial View Create Employee

@model MMV.Models.Employee

@using (Html.BeginForm("Create","Employees"))
{
    <table class="table">

        @Html.ValidationSummary(true, "", new { @class = "text-danger" })

        <tr>
            <td>
                @Html.EditorFor(model => model.name)
                @Html.ValidationMessageFor(model => model.name, "", new { @class = "text-danger" })
            </td>
            <td>
                @Html.EditorFor(model => model.jobtitle)
                @Html.ValidationMessageFor(model => model.jobtitle)
            </td>
            <td>
                @Html.EditorFor(model => model.number)
                @Html.ValidationMessageFor(model => model.number, "", new { @class = "text-danger" })
            </td>
            <td>
                @Html.EditorFor(model => model.address)
                @Html.ValidationMessageFor(model => model.address, "", new { @class = "text-danger" })
            </td>
        </tr>
    </table>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Create" class="btn btn-default" />
        </div>
    </div>
}

Controller - I used multiple but you can put them all in one

public class CompanyEmployeeController : Controller
{
    private MyContext db = new MyContext();

    // GET: CompanyEmployee
    public ActionResult Index()
    {
        var newCompanyEmployee = new CompanyEmployee();
        newCompanyEmployee.employees = db.EmployeeContext.ToList();
        return View(newCompanyEmployee);
    }

    [HttpPost, ActionName("Delete")]
    public ActionResult DeleteConfirmed(int id)
    {
        Employee employee = db.EmployeeContext.Find(id);
        db.EmployeeContext.Remove(employee);
        db.SaveChanges();
        return RedirectToAction("Index", "CompanyEmployee");
    }

    [HttpPost]
    public ActionResult Create([Bind(Include = "id,name,jobtitle,number,address")] Employee employee)
    {
        if (ModelState.IsValid)
        {
            db.EmployeeContext.Add(employee);
            db.SaveChanges();
            return RedirectToAction("Index", "CompanyEmployee");
        }

        return View(employee);
    }
}

Updated Code - using BeginCollectionItem - dynamic add/delete

Student Partial

@model UsefulCode.Models.Person
<div class="editorRow">
    @using (Html.BeginCollectionItem("students"))
    {
        <div class="ui-grid-c ui-responsive">
            <div class="ui-block-a">
                <span>
                    @Html.TextBoxFor(m => m.firstName)
                </span>
            </div>
            <div class="ui-block-b">
                <span>
                    @Html.TextBoxFor(m => m.lastName)
                </span>
            </div>
            <div class="ui-block-c">
                <span>
                    <span class="dltBtn">
                        <a href="#" class="deleteRow">X</a>
                    </span>
                </span>
            </div>
        </div>
    }
</div>

Teacher Partial

@model UsefulCode.Models.Person
<div class="editorRow">
    @using (Html.BeginCollectionItem("teachers"))
    {
        <div class="ui-grid-c ui-responsive">
            <div class="ui-block-a">
                <span>
                    @Html.TextBoxFor(m => m.firstName)
                </span>
            </div>
            <div class="ui-block-b">
                <span>
                    @Html.TextBoxFor(m => m.lastName)
                </span>
            </div>
            <div class="ui-block-c">
                <span>
                    <span class="dltBtn">
                        <a href="#" class="deleteRow">X</a>
                    </span>
                </span>
            </div>
        </div>
    }
</div>

Register Controller

public ActionResult Index()
{
    var register = new Register
    {
        students = new List<Person>
        {
            new Person { firstName = "", lastName = "" }
        },
        teachers = new List<Person> 
        {
            new Person { lastName = "", firstName = "" }
        }
    };

    return View(register);
}

Register and Person Model

public class Register
{
    public int id { get; set; }
    public List<Person> teachers { get; set; }
    public List<Person> students { get; set; }
}

public class Person
{
    public int id { get; set; }
    public string firstName { get; set; }
    public string lastName { get; set; }
}

Index

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}
@model UsefulCode.Models.Register
<div id="studentList">
@using (Html.BeginForm())
{
    <div id="editorRowsStudents">
        @foreach (var item in Model.students)
        {
            @Html.Partial("StudentView", item)
        }
    </div>
    @Html.ActionLink("Add", "StudentManager", null, new { id = "addItemStudents", @class = "button" });
}
</div>

<div id="teacherList">
@using (Html.BeginForm())
{
    <div id="editorRowsTeachers">
        @foreach (var item in Model.teachers)
        {
            @Html.Partial("TeacherView", item)
        }
    </div>
    @Html.ActionLink("Add", "TeacherManager", null, new { id = "addItemTeachers", @class = "button" });
}
</div>


@section scripts {
    <script type="text/javascript">
    $(function () {
        $('#addItemStudents').on('click', function () {
            $.ajax({
                url: '@Url.Action("StudentManager")',
                    cache: false,
                    success: function (html) { $("#editorRowsStudents").append(html); }
                });
                return false;
            });
            $('#editorRowsStudents').on('click', '.deleteRow', function () {
                $(this).closest('.editorRow').remove();
            });
            $('#addItemTeachers').on('click', function () {
                $.ajax({
                    url: '@Url.Action("TeacherManager")',
                    cache: false,
                    success: function (html) { $("#editorRowsTeachers").append(html); }
                });
                return false;
            });
            $('#editorRowsTeachers').on('click', '.deleteRow', function () {
                $(this).closest('.edi

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...