I have made changes below to the question, which is still the same but hopefully a lot clearer through the models and in regards to what I want to achieve and where I've come up against issues.
Below are shown two classes, Company and Employee, Company has a list of Employees.
This will be an input form so there will be no data in there to begin with.
Ultimately I want the user to be able to add as many Employee objects to the Company object model as they want and for the Employee objects to be updated
Am I on the right track with using BeginCollectionItem so I can add/remove as many Employee objects as I want? When I click on the Add button it takes it to the partial view on another page (with AjaxActionLink) but not with JavaScript.
Update Removed AjaxActionLink and used JavaScript instead.
Index
@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;
});
}
Controller
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);
}
}
Model
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;}
}
You do not need to use BeginCollectionItem in order to achieve this. From having to look into it myself and trying to use it for a similar issue, it appears it was created for problems of this nature with earlier versions of MVC.
Use Partial Views to display and update the list. One partial view to display and iterate through the list of objects, and another to create a new object which upon post back to update the list will show the newly created object in the partial view with the list.
I posted a similar question on here which should solve your issue, click here
Hope this helps.
Update The reason your delete doesn't work is because you can't call JS from Partial View, put it in the main view (@section Script). Also I think you got a bit muddled with your class and id keywords in your divs, have a look below.
So you should have:
Partial View
Main View
Lets say
ClassA
is your ViewModel:You only need one Partial View or View to Update both:
e.g.
Edit.cshtml
Note: In your Partial view you are using foreach loop, The MVC Model Binder Requires the Input fields to be in the format:
So for this we use for loop
Then in controller:
If you want to dynamically Add or Remove Items from the List:
Create another Partial View
classBs
:Create another Partial View:
ClassBRow.cshtml
:In your controller:
AND Edit.cshtml becomes:
You can use (EditorTemplates) to view (ClassB) as following:
1- Create folder named (EditorTemplates) under the (Views/Home) folder (Assuming your controller name is Home):
2- Under the created (EditorTemplates) folder, create a view named (ClassB)
and add the following template for the (ClassB) view:
and (ClassAView) should be as following:
The editor will automatically iterate through the list of objects rendering the view for each of them.
Treat the views as independent. If your partial view was a full view, you would pass it a
IEnumerable<ClassB>
so render with:
Also, you can't use
foreach
withEditorFor
as there is insufficient information to create a name based on the index. Use a normal for loop instead. The expression parser can then convert it toname="name[1]"
etc as the index is available in the expression:There is more missing from your example (what
Clear
andCreate
connect to for instance), so if you can provide the rest of your controller code I will expand this.