I am an experienced .NET C# Software Developer, however only a few months ago i started worked with MVC Razor (MVC 5).
I have a small situation that i couldn't find any answer to on the net(after hours of searching)
I have a Model that has a list of another Model which in turn also has a list of a model as shown below.
public class Parent
{
public string Name { get; set; }
public string Sex { get; set; }
public int Age { get; set; }
public List<Child> Children { get; set; }
}
public class Child
{
public string Name { get; set; }
public string Sex { get; set; }
public int Age { get; set; }
public List<GrandChild> GrandChildren { get; set; }
}
public class GrandChild
{
public string Name { get; set; }
public string Sex { get; set; }
public int Age { get; set; }
}
and my Controller has 2 methods, one is main Get, and the other one is post in order to post new data
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult PostParent(Parent parent)
{
if (ModelState.IsValid)
{
//Do an insert to the DB
return View("~/Views/Home/Index.cshtml");
}
return Json("Error");
}
However in my View in Form, i cannot find a way to create an add button and insert new data to the list of Children and GrandChildren(incase of a Child)
@using (Html.BeginForm("PostParent", "Home", FormMethod.Post, new {@class = "form-horizontal", role = "form"}))
{
@Html.LabelFor(x => x.Name)
@Html.TextBoxFor(x => x.Name)
}
I can only add fields for primitive type properites, but not for Objects.
I would really appreciate any Help!
For this you can use Html.BeginCollectionItem https://www.nuget.org/packages/BeginCollectionItem/
What you can do is have a View for the Main Form and Pass the Parent
Model to it as this will represent the Parent, then you will need a row to represent a Child, you can call this ChildRow.cshtml
So for the Parent we are creating Create.cshtml
View which we will pass the Parent Model.
@model Parent
@using(Html.BeginForm())
{
@Html.TextBoxFor(m => m.Name)
<table id="children">
<tbody>
<!-- This here will display the Child Row for existing Rows in the Parent model -->
@foreach (var child in Model.Children )
{
@Html.Partial("ChildRow", child)
}
</tbody>
</table>
<button type="button" id="addChild">Add Child</button>
<button type="submit"> Save</button>
}
Then this is what the ChildRow.cshtml will look like, It will have a Child
model.
Note: you will have to add IsDeleted property to child Model, this will help you in the controller - to see if Child was deleted or Not.
@model Child
@using (Html.BeginCollectionItem("Children"))
{
<tr>
<td>
@Html.HiddenFor(m => m.IsDeleted, new { data_is_deleted = "false" })
@Html.HiddenFor(m => m.ChildId)
@Html.TextBoxFor(m => m.Name )
</td>
<td>
<span class="glyphicon glyphicon-trash action-icon" data-action="removeItem"></span>
</td>
<tr>
}
Now you have to add a Row to the end of the Table when the button Add Child
is clicked.
For this you will create a new action in the Controller:
public ActionResult AddChild()
{
return PartialView("Child", new Child());
}
And then add this jQuery to the Create.cshtml
$("#addChild").on("click", function () {
$.ajax({
url: '@Url.Action("AddChild", "YourController")'
}).success(function (partialView) {
$('#children> tbody:last-child').append(partialView);
});
});
this will append a new Child Row to the Table
Also you will have to hide/disable the row if you want to delete on the same page aswell, for that you can add this jQuery:
$('body').on("click", '*[data-action="removeItem"]', function (e) {
e.stopPropagation();
var btn = $(this);
var row = btn.closest('tr');
var firstCell = $(row.find('td')[0]);
var checkBox = firstCell.find('*[data-is-deleted]');
var checkBoxVal = checkBox.val();
if (checkBoxVal === 'False' || checkBox.val() === 'false') {
checkBox.val('true');
row.find('td').find('input, textarea, select').attr('readonly', 'readonly');
} else {
checkBox.val('false');
row.find('td').find('input, textarea, select').attr("readonly", false);
}
});
Then when you POST back to the Controller you will see the List of Children in the Model.
[HttpPost]
public ActionResult Create(Parent model)
{
var newChildren = model.Children.Where(s => !s.IsDeleted && s.ChildId == 0);
var updated = model.Children.Where(s => !s.IsDeleted && s.ChildId != 0);
var deletedChildren = model.Children.Where(s => s.IsDeleted && s.ChildId != 0);
return View(model);
}
You will have to do something similar for the GrandChildren.
I think you need a usual Standard <input/>
which you should know from HTML5.
If I understood you correctly you want to pass the data from the view to the Controller. Now you should need a button in the form you've created.
You need/should call a Controller by passing the model-data from the view to the Controller. @Html.ActionLink()
is a Razor-Helper for <Input>
Something like this inside the form may should do the trick:
@Html.ActionLink("Click here to pass", "ActionName", new { ParameterNameOfTheController = Model })
The logic behind that stuff should happen in the Controller. I hope this can help you.
I did a project related to your scenario. However, I couldn't find a razor solution. So i ended up using SheepIt jquery plugin to dynamically add fields.
https://github.com/mdelrosso/sheepit
Make sure you stick to id and name pattern which asp mvc understands.
<input class="form-control text-box single-line" id="sheepItForm_#index#__Field" name="sheepItForm[#index#].Field" type="text" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="sheepItForm[0].Field" data-valmsg-replace="true"></span>
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With