I've just started my journey with MVC, but I have a problem and for two days I can't find answer.
I have EditorFor and ListBoxFor in a one site and I want to Post info from EditorFor about Name and from ListBoxFor about EmployeeId.
In listbox I have list of my employees. I can write name in Editor and I can choose one of employee from list.
Everything is ok, but when I mark anything inside ListBox it makes that text inside EditorFor disappears.
View:
#model Models.CreateClient
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div>
<h4>Client</h4>
<hr />
<div class="col-md-6">
#Html.ValidationSummary(true, "", new {#class = "text-danger"})
<div class="form-group">
#Html.LabelFor(model => model.Client.Name, htmlAttributes: new {#class = "control-label col-md-2"})
<div class="col-md-10">
#Html.EditorFor(model => model.Client.Name, new {htmlAttributes = new {#class = "form-control"}})
#Html.ValidationMessageFor(model => model.Client.Name, "", new {#class = "text-danger"})
</div>
</div>
</div>
<div class="col-md-6">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.SelectedEmployee, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.ListBoxFor(model => model.SelectedtEmployee, Model.EmployeeListItem(), new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SelectedEmployee, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group col-md-6">
<input type="submit" value="CreateNew" class="btn btn-default " />
#Html.ActionLink("Back to List", "Index")
</div>
</div>
}
Model:
public class CreateClient
{
public Client Client { get; set; }
public List<Employee> Employees { get; set; }
public IEnumerable<int> SelectedtEmployee { get; set; }
public IEnumerable<SelectListItem> EmployeeListItem()
{
List<SelectListItem> employeeSelectedListItem = new List<SelectListItem>();
foreach (var employee in Employees)
{
SelectListItem selectListItem = new SelectListItem()
{
Text = employee.FullName,
Value = employee.Id.ToString(),
};
employeeSelectedListItem.Add(selectListItem);
}
return employeeSelectedListItem;
}
}
Controller:
public ActionResult CreateNew()
{
CreateClient createClient = new CreateClient();
createClient.Employees = db.EmployeeContet.ToList();
return View(createClient);
}
I made mistake and my Client's name was int (instead of string).
Related
I am new to ASP.NET MVC 5 and having some issues with binding only the multi-select section of the body during POST when submitting the form. The form renders correctly, with checkboxes being correctly selected. The form is scaffolded using visual studio (except the multi-select)
public class EditViewModel
{
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "LastName")]
public string LastName { get; set; }
public IList<RolesViewModel> Roles { get; set; }
}
public class RolesViewModel
{
public string RoleId { get; set; }
public string RoleName { get; set; }
public bool Selected { get; set; }
}
[HttpGet]
public async Task<ActionResult> Edit(string Id)
{...
var model = Mapper.Map<ApplicationUser, EditViewModel>(user);
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(EditViewModel model)
{
}
#model BloggingService.Web.Models.EditViewModel
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Edit</title>
</head>
<body>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Edit</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FirstName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LastName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.LastName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LastName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Roles, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#for (int i = 0; i < Model.Roles.Count; i++)
{
#Html.CheckBoxFor(x => #Model.Roles[i].Selected,"test1");
<div class="form-check">
<input type="hidden" asp-for="#Model.Roles[i].RoleId" />
<input type="hidden" asp-for="#Model.Roles[i].RoleName" />
<input type="checkbox" asp-for="#Model.Roles[i].Selected" class="form-check-input" checked="#Model.Roles[i].Selected" />
<label class="form-check-label" asp-for="#Model.Roles[i].Selected">
#Model.Roles[i].RoleName
</label>
</div>
}
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Would really appreciate any insight.
I have managed to find the correct and cleaner approach for accomplishing such tasks by utilizing the HTML helper methods inside my for loop and now the data are able to bind correctly during data-binding.
#Html.HiddenFor(x => #Model.Roles[i].RoleId);
#Html.HiddenFor(x => #Model.Roles[i].RoleName);
#Html.CheckBoxFor(x => #Model.Roles[i].Selected);
I am working on an MVC application, which has the stages D1, D2, D3 and D4 and we will load/edit the each stages from the application.
Right now, I have created separate models and separate views for each stage model like below and utilizing it. But for code reuse, I would like to use common view for all stages (D1, D2, D3 and D4) since all the stages contains same columns.
I have an idea to implement by creating a single view-model for all four stages and utilizing but we need to edit and save the values separately. So I am struck with that point. Is there any idea to use a common view for all four models by loading and saving the data for each stage.
Entities
public partial class D1Stage : EntityBase
{
[DisplayName("Status Indicator")]
public byte StatusIndicatorId { get; set; }
[DisplayName("Status Info")]
public string StatusInfo { get; set; }
[DisplayName("Completed %")]
[SCIRange(0, 100)]
public decimal StageCompletion { get; set; }
[DisplayName("Is Step Mandatory")]
public bool IsMandatory { get; set; }
}
And as I mentioned, D2Stage, D3Stage and D4Stage have exactly the same properties, for example for D2Stage:
public partial class D2Stage : EntityBase
{
... exact same properties as D1Stage
}
Views
#model Brain.DAL.Entities.D1Stage
#{
IEnumerable<SelectListItem> StatusIndicators = ViewBag.StatusIndicators;
}
#using (Html.BeginForm("", "", FormMethod.Post, new { enctype = "multipart/form-data", #class = "form form-horizontal" }))
{
<div class="form-body">
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.Id)
#Html.HiddenFor(model => model.Report.Id)
<div class="form-group">
#Html.SCILabelFor(model => model.StatusIndicatorId, new { #class = "col-md-1 control-label" })
<div class="input-group col-md-4">
#Html.DropDownListFor(model => model.StatusIndicatorId, new SelectList(StatusIndicators, "Value", "Text"), "None", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.StatusIndicatorId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.SCILabelFor(model => model.StatusInfo, new { #class = "col-md-1 control-label" })
<div class="input-group col-md-4">
#Html.TextAreaFor(model => model.StatusInfo, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.StatusInfo, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.SCILabelFor(model => model.StageCompletion, new { #class = "col-md-1 control-label" })
<div class="input-group col-md-4">
#Html.SCINumericTextBoxFor(model => model.StageCompletion, new { #class = "form-control sliderrangemintext", #style = "width:100px" })
<div class="sliderrangemin" id="slider-range-min" style="margin-top:50px"></div>
</div>
</div>
<div class="form-group">
#Html.SCILabelFor(model => model.IsMandatory, new { #class = "col-md-1 control-label" })
<div class="input-group col-md-4">
<div class="input-group" style="width:100%">
#Html.DropDownListFor(model => model.IsMandatory, new List<SelectListItem>() { new SelectListItem { Text = "No", Value = "false" }, new SelectListItem { Text = "Yes", Value = "true", Selected = true } }, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.IsMandatory, "", new { #class = "text-danger" })
</div>
</div>
</div>
</div>
}
And as I mentioned above, all the other views have the same content with just having different corresponding model. For example for D2Stage
#model Brain.DAL.Entities.D2Stage
... The difference is just in model.
... Rest of the content is exactly the same as D1Stage view
All models have similar properties? Assuming you cannot create a single model. Then move the properties to base class and create the other models deriving from the base model. Then also create a single view having base model as model.
Models
public class MyBaseModel
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class ModelA: MyBaseModel{}
public class ModelB: MyBaseModel{}
View
Create a single view for the MyBaseModel and Put the view in the Shared folder and name it something like MySharedView:
#model MyNamespace.MyBaseModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Title which you can decide based on model type or get from ViewBag</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Property1, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Property1, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Property1, "", new { #class = "text-danger" })
</div>
</div>
... Rest of properties ...
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Controller
Then in controllers, when you want to return view, specify the shared view name. For example for model A and the same for model B:
[HttpGet]
public ActionResult A()
{
var model = new ModelA() { Property1 = "A", Property2 = "A" };
return View("MySharedView", model);
}
[HttpPost]
public ActionResult A(ModelA model)
{
// SaveA(model)
// Redirect to index
}
It seems you should use the pattern ModelFactory.
Create an Interface IStage with properties and method (only declaretions). Then All the class D*Stage implements IStage. So the View has as model IStage.
In this way every class can do diffent things in each method.
interface IStage{
byte StatusIndicatorId { get; set; }
string StatusInfo { get; set; }
decimal StageCompletion { get; set; }
bool IsMandatory { get; set; }
}
public partial class D1Stage : EntityBase, IStage
{
[DisplayName("Status Indicator")]
public byte StatusIndicatorId { get; set; }
[DisplayName("Status Info")]
public string StatusInfo { get; set; }
[DisplayName("Completed %")]
[SCIRange(0, 100)]
public decimal StageCompletion { get; set; }
[DisplayName("Is Step Mandatory")]
public bool IsMandatory { get; set; }
}
#View
#model IStage
#{
IEnumerable<SelectListItem> StatusIndicators = ViewBag.StatusIndicators;
}
#using (Html.BeginForm("", "", FormMethod.Post, new { enctype = "multipart/form-data",
#class = "form form-horizontal" }))
{
<div class="form-body">
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.Id)
#Html.HiddenFor(model => model.Report.Id)
<div class="form-group">
#Html.SCILabelFor(model => model.StatusIndicatorId, new { #class = "col-md-1 control-label" })
<div class="input-group col-md-4">
#Html.DropDownListFor(model => model.StatusIndicatorId, new SelectList(StatusIndicators, "Value", "Text"), "None", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.StatusIndicatorId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.SCILabelFor(model => model.StatusInfo, new { #class = "col-md-1 control-label" })
<div class="input-group col-md-4">
#Html.TextAreaFor(model => model.StatusInfo, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.StatusInfo, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.SCILabelFor(model => model.StageCompletion, new { #class = "col-md-1 control-label" })
<div class="input-group col-md-4">
#Html.SCINumericTextBoxFor(model => model.StageCompletion, new { #class = "form-control sliderrangemintext", #style = "width:100px" })
<div class="sliderrangemin" id="slider-range-min" style="margin-top:50px"></div>
</div>
</div>
<div class="form-group">
#Html.SCILabelFor(model => model.IsMandatory, new { #class = "col-md-1 control-label" })
<div class="input-group col-md-4">
<div class="input-group" style="width:100%">
#Html.DropDownListFor(model => model.IsMandatory, new List<SelectListItem>() { new SelectListItem { Text = "No", Value = "false" }, new SelectListItem { Text = "Yes", Value = "true", Selected = true } }, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.IsMandatory, "", new { #class = "text-danger" })
</div>
</div>
</div>
</div>
}
In this way there's only one view.
I have create four models, then i have created a viewmodel that contains first four, i have created a view with controller that show this viewmodel;
When i created Actioresult Create (http post), i on't know how pass entire model.
viewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Avvisi.Models
{
public class PianoCompletoViewModels
{
public Guid Id { get; set; }
public DatiPiano DatiPiano { get; set; }
public DescrizionePiano descrizionePiano { get; set; }
public ImpresaBeneficiaria impresaBeneficiaria { get; set; }
public ResponsabilePiano responsabilePiano { get; set; }
public ProspettoFinanziario prospettoFinanziario { get;set; }
}
}
controller:
public ActionResult Create()
{
string UserID = Convert.ToString(User.Identity.GetUserId());
int AvvisoID = Convert.ToInt32(Session["AvvisoID"]);
ViewBag.FKCompartoID = new SelectList(db.Comparto, "CompartoID",
"Nome");
ViewBag.PianoID = new SelectList(db.DescrizionePiano,
"DescrizioneID", "PresentImpBenef_RelContAziend");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(PianoCompletoViewController PianoView)
{
if (ModelState.IsValid)
{
return View();
}
return View();
}
I expect that when i click on submit there is a model filled wiht property(other mmodels) For Example:
PianoView.model1.xxx
PianoView.model2.xxx
PianoView.model3.xxx
PianoView.model4.xxx
cshtml view:
#model Avvisi.Models.PianoCompletoViewModels
#{
ViewBag.Title = "Create";
}
<h2>Dati Piano</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Dati Piano</h4>
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row" style="margin-left:0px;margin-right:0px">
<div class="form-group">
#Html.LabelFor(model => model.DatiPiano.NomePiano, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DatiPiano.NomePiano, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.DatiPiano.NomePiano, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.descrizionePiano.PresentImpBenef_RelContAziend, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.descrizionePiano.PresentImpBenef_RelContAziend, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.descrizionePiano.PresentImpBenef_RelContAziend, "", new { #class = "text-danger" })
</div>
</div>
<div class="row">
<div class="form-group">
#Html.LabelFor(model => model.impresaBeneficiaria.CompanyName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.impresaBeneficiaria.CompanyName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.impresaBeneficiaria.CompanyName, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.responsabilePiano.Nome, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.responsabilePiano.Nome, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.responsabilePiano.Nome, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" style="width:95%;background: #dedede;" />
</div>
</div>
</div>
}
I don't understand why it no let me to choose right properties(Models).
Can you help me, please???
You have to use html control name like this:
<input name="descrizionePiano.someProperty">
Then surely you have got value for it.
My Controller Method:
[HttpGet]
public ActionResult NewInventory()
{
return View();
}
[HttpPost]
public async Task<ActionResult> NewInventory(string bookID, string ttlin, string lowin, string Outnow)
{
// test values passed, etc.....
}
So far only the "lowin" value is being passed correctly. All the other values are set to "0" (I believe due to the datatypes being set to "not null" in SQL DB). Why is this?
I assume because only one value is passed correctly and no exceptions are thrown, then the view page code is missing the other the fields to pass.
View Code :
#model LibraryMS.Inventory
#{
ViewBag.Title = "newinventory";
}
<h2>newinventory</h2>
#using (Html.BeginForm("NewInventory","BookInfo", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Inventory</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.BookID, "BookID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.BookID, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.BookID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TotalIn, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TotalIn, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TotalIn, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LowIn, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.LowIn, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LowIn, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Out, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Out, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Out, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
By looking, the values are being passed.
Turns out that the controller method's parameters needs to be spelled the same as what is going to be passed to it. For example:
With the autogenerated form using html helper #Beginform, all the fields are present. But the parameters in the controller method are not the same as the inventory's class fields.
public partial class Inventory
{
public string BookID { get; set; }
public short TotalIn { get; set; }
public short LowIn { get; set; }
public short Out { get; set; }
public virtual BookInfo BookInfo { get; set; }
}
Compared to the paramters:
[HttpPost]
public async Task<ActionResult> NewInventory(string bookID, string ttlin, string lowin, string Outnow)
{
// test values passed, etc.....
}
The fix was to ofcoarse make the params the same, capitalization does not matter.
[HttpPost]
public async Task<ActionResult> NewInventory(string bookID, string totalin, string lowin, string Out)
{
// test values passed, etc.....
}
It was a simple mistake but took me some time to figure this out. Hopefully this helped someone else!
I have a model that has a complex type as a property. I have created a custom DisplayEditor for the complex child type, and it is being bound correctly when the page is loaded. When the page is posted after edits have been made, the Dependents type is being set to null. Here is the code for the Employee model that represents the child Dependents property:
[Display(Name = "Dependents")]
[DataType(DataType.MultilineText)]
public List<Dependent> Dependents { get; set; }
Here is the Dependent model:
[Serializable]
public class Dependent : Person
{
public Dependent()
{
Deduction deduction = new Deduction(this) { Amount = Constants.DependentDeductionAmount };
this.Deduction = deduction;
}
[Key]
[HiddenInput(DisplayValue = false)]
public int DependentId { get; set; }
[Required]
[Display(Name = "Dependent Type")]
public DependentType DependentType { get; set; }
[Required]
public override double DeductionAmount => Constants.DependentDeductionAmount;
}
The 2 edit action methods on the employee controller (I've tried TryUpdateModel, doesn't work):
public ViewResult Edit(int employeeId)
{
if (employeeId < 0) throw new ArgumentOutOfRangeException(nameof(employeeId));
Employee employee = _employeeRepository.Employees.FirstOrDefault(e => e.EmployeeId == employeeId);
bool result = TryUpdateModel(employee, new FormValueProvider(ControllerContext));
return View(employee);
}
[HttpPost]
public ActionResult Edit(Employee employee)
{
if (employee == null) throw new ArgumentNullException(nameof(employee));
if (ModelState.IsValid)
{
employee.Changed = true;
employee.Dependents.ForEach(d => d.Changed = true);
_employeeRepository.SaveEmployee(employee);
TempData["message"] = $"{employee} has been saved.";
return RedirectToAction("Index");
}
else {
// there is something wrong with the data values
return View(employee);
}
}
Here is the Edit.cshtml:
#model Paylocity.HR.Domain.Entities.Employee
#{
ViewBag.Title = $"{"Edit"} {Model}";
}
<div class="panel panel-default">
<div class="panel-heading">
<h3>#ViewBag.Title</h3>
</div>
#using (Html.BeginForm("Edit", "Employee"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr/>
#Html.ValidationSummary(true, "", new {#class = "text-danger"})
<h4>Employee</h4>
<div class="form-group">
#Html.LabelFor(model => model.FirstName, htmlAttributes: new {#class = "control-label col-md-2"})
<div class="col-md-10">
#Html.EditorFor(model => model.FirstName, new {htmlAttributes = new {#class = "form-control"}})
#Html.ValidationMessageFor(model => model.FirstName, "", new {#class = "text-danger"})
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LastName, htmlAttributes: new {#class = "control-label col-md-2"})
<div class="col-md-10">
#Html.EditorFor(model => model.LastName, new {htmlAttributes = new {#class = "form-control"}})
#Html.ValidationMessageFor(model => model.LastName, "", new {#class = "text-danger"})
</div>
</div>
<hr/>
#Html.EditorFor(model => model.Dependents, "Dependents")
#Html.HiddenFor(model => model.EmployeeId)
</div>
<div class="panel-footer">
<input type="submit" value="Save" class="btn btn-primary"/>
#Html.ActionLink("Cancel and return to List", "Index", null, new {#class = "btn btn-default"})
</div>
}
</div>
Here is the Dependent.cshtml EditorTemplate:
#model IEnumerable<Dependent>
#using Paylocity.HR.Domain.Entities
#foreach (var dep in Model)
{
<h4>Dependent</h4>
<div class="form-group">
#Html.LabelFor(m => dep.FirstName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(m => dep.FirstName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => dep.FirstName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => dep.LastName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(m => dep.LastName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => dep.LastName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => dep.DependentType, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EnumDropDownListFor(m => dep.DependentType, new { #class = "form-control" })
#Html.ValidationMessageFor(m => dep.DependentType, "", new { #class = "text-danger" })
</div>
</div>
<hr />
}
The employee object binds correctly and is updateable, it's only the dependents child type that isn't being bound correctly. The HTML is displaying the correct ID's/names for the Dependent form fields (I believe?). Do I need to implement some sort of custom binder code, or am I missing something obvious here?
This is my first question on SO, I hope I provided enough information.
Change the model in the Dependent.cshtml template to #model Dependent (it cant be IEnumerable<T>) and remove the foreach loop which is generating name attributes which have no relationship to your model (and duplicate id attributes which is invalid html)
It also needs be located in the /Views/Shared/EditorTemplates/ or /Views/yourControllerName/EditorTemplates/ folder
#model Dependent
...
#Html.EditorFor(m => m.FirstName, new { htmlAttributes = new { #class = "form-control" } })
...
#Html.EditorFor(m => m.LastName, new { htmlAttributes = new { #class = "form-control" } })
etc. Then in the main view, use
#Html.EditorFor(model => model.Dependents)
The EditorFor() method accepts IEnumerable<T> and will generate the correct html for each item in the collection including the correct name attributes with indexers
I think your problem is the way you generating the list of item.
use index instead of foreach.
hence your Dependent.cshtml EditorTemplate
do something like:
#for(int i = 0; i < Model.Count(); i++)
{
<h4>Dependent</h4>
<div class="form-group">
#Html.LabelFor(m => Model[i].FirstName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(m => Model[i].FirstName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => Model[i].FirstName, "", new { #class = "text-danger" })
</div>
</div>
// rest field follow same pattern
}
for more info about binding list, checkout this post
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/