ASP.NET MVC POST won't send back second ocurrence of variable - c#

The class Unidade:
public class Unidade
{
public int UnidadeId { get; set; }
public string Apelido { get; set; }
public string Descricao { get; set; }
}
Is used twice in the class Insumo, as Unidade and UnidadeConsumo
public class Insumo
{
public int InsumoId { get; set; }
public string Apelido { get; set; }
public string Descricao { get; set; }
public int UnidadeId { get; set; }
public Unidade Unidade { get; set; }
public int UnidadeConsumoId { get; set; }
public Unidade UnidadeConsumo { get; set; }
}
To edit Insumo there are two actions EDIT in the controller:
public ActionResult Edit(int? id)
{
Insumo insumo = db.Insumos.Find(id);
if (insumo == null) return HttpNotFound();
ViewBag.UnddId = new SelectList(db.Unidades, "UnidadeId", "Apelido", insumo.UnidadeId);
ViewBag.UndConsId = new SelectList(db.Unidades, "UnidadeId", "Apelido", insumo.UnidadeConsumoId);
return View(insumo);
}
And the POST EDIT:
[HttpPost]
public ActionResult Edit([Bind(Include = "InsumoId,Apelido,Descricao,UnidadeId,UnidadeConsumoId")] Insumo insumo)
{
if (ModelState.IsValid)
{
db.Entry(insumo).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.UnddId = new SelectList(db.Unidades, "UnidadeId", "Apelido", insumo.UnidadeId);
ViewBag.UndConsId = new SelectList(db.Unidades, "UnidadeId", "Apelido", insumo.UnidadeConsumoId);
return View(insumo);
}
The view to display the fields to edition, comprised of two dropdown lists to select both units is:
#model Gestor.Models.Insumo
#{
ViewBag.Title = "Alterar";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Alterar</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Insumo</h4>
<hr />
#Html.Partial("CopyEdit")
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Gravar" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Retornar a lista", "Index")
</div>
And the partial view CopyEdit in the center:
#model Gestor.Models.Insumo
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.InsumoId)
<div class="form-group">
#Html.LabelFor(model => model.Apelido, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Apelido, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Apelido, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Descricao, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Descricao, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Descricao, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.UnidadeId, "Unidade", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("UnddId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.UnidadeId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.UnidadeConsumoId, "Unidade de Consumo", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("UndConsId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.UnidadeConsumoId, "", new { #class = "text-danger" })
</div>
</div>
The problem is thta whrn returned to the POST Edit action, all fields are ok, but UnidadeConsumoId is always 0, what doesn't even exists in the database?
Can someone tell me why is it not returning the expected value, i.e. the selected value in the dropdown list reflecting the id of if?

Because your current code is rendering a SELECT element with name attribute value UndConsId.
<select class="form-control" name="UndConsId">
<!-- options -->
</select>
For model binding to work, the input element name attribute value should match with the parameter/property name used in the http post action method. Your parameter name is UnidadeConsumoId, not UndConsId
To fix this issue, pass UnidadeConsumoId as the first parameter of the DropDownList method call so that it will render the SELECT element with name UnidadeConsumoId. You can pass the ViewBag.UndConsId as the second parameter to explicitly specify the collection to be used to build the SELECT element.
This should work
#Html.DropDownList("UnidadeConsumoId", ViewBag.UndConsId as SelectList,
new { #class = "form-control" })

Related

when clicking on create new hole Would like to be able to select a courseId

I am trying to create a golf score app. I have created the database that has the following tables Round, Course and Hole.
The hole table has a foreign key to the courseId as when I create each hole I want to link it to a course.
When clicking on the create new I would like to have a dropdown list to select the courseId from when inputting all the hole details.
But when I click on the create new I get the following error in the Hole view model.
System.ArguementNullException: Value can not be Null. Parameter name items. in my Create action code within the HoleViewModelController.
This is my HoleViewModel code
public class HoleViewModel
{
[Key]
public int HoleId { get; set; }
public int HoleNumber { get; set; }
public int Par { get; set; }
public int Length { get; set; }
public int StrokeIndex { get; set; }
[ForeignKey("Course")]
public int? CourseId { get; set; }
public IEnumerable<SelectListItem> CourseNamesDropdownList { get; set; }
public virtual CourseViewModel Course { get; set; }
}
}
This is my CourseViewModel code
public class CourseViewModel
{
[Key]
public int CourseId { get; set; }
public string CourseName { get; set; }
This is my HoleViewModelController Create action
public ActionResult Create()
{
ViewBag.CourseId = new SelectList(db.CourseViewModels, "CourseId", "CourseName");
return View();
}
This is my MVC Create view
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>HoleViewModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.HoleNumber, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.HoleNumber, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.HoleNumber, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Par, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Par, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Par, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Length, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Length, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Length, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StrokeIndex, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StrokeIndex, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StrokeIndex, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CourseId, "CourseId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Course.CourseId, Model.CourseNamesDropdownList, "Please select from List", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CourseId, "", 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>
Ok so i resolved this issue by ammending the ActionResult Create code from
public ActionResult Create()
{
ViewBag.CourseId = new SelectList(db.CourseViewModels, "CourseId", "CourseName");
return View();
}
To
public ActionResult Create()
{
var dbcourse = db.Course.ToList();
//Make selectlist, which is IEnumerable<SelectListItem>
var courseNameDropdownList = new SelectList(db.Course.Select(item => new SelectListItem()
{
Text = item.CourseName.ToString(),
Value = item.CourseId.ToString()
}).ToList(), "Value", "Text");
// Assign the Selectlist to the View Model
var viewCourse = new HoleViewModel()
{
Course = dbcourse.FirstOrDefault(),
// The Dropdownlist values
CourseNamesDropdownList = courseNameDropdownList,
};
//ViewBag.CourseId = new SelectList(db.CourseViewModels, "CourseId", "CourseName");
return View(viewCourse);
}
This has allowed the dropdown to be populated and be added to the view.

How to do a model binding of an entity that contains a list of another entity?

I want make a form using a model that contains a list of another entity, but I don't know how to do it. Note: without Entity Framework.
I have use only a model that contains only its fields, but not another entity.
My code is:
Tarea.cs
public class Tarea
{
public int ID { get; set; }
public string NombreTarea { get; set; }
public int TiempoTarea { get; set; }
}
Employee.cs
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime JoiningDate { get; set; }
public int Age { get; set; }
public List<Tarea> Tareas { get; set; }
}
And my view is:
Create.cshtml
#model MVCSimpleApp.Models.Employee
....
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Employee</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.JoiningDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.JoiningDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.JoiningDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Age, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Age, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Age, "", 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>
}
I don't know how to put the List<Tarea> in the form. Thanks in advance.
For Example, if we want to collect two sets of Tareas model,
View Code:
#for (var item = 0; item < 2; item++)
{
#Html.HiddenFor(model => model.Tareas[item].ID)
#Html.TextBoxFor(model => model.Tareas[item].NombreTarea, new { #class = "form-control" })
#Html.TextBoxFor(model => model.Tareas[item].TiempoTarea, new { #class = "form-control" })
}
And in the Post Create Action Method..
[HttpPost]
public ActionResult Create(Employee model)
{
// here we we get the all values given from the view ...
string example = model.Name;
List<Tarea> list = model.Tareas; // and iterate through model.Tareas to get List model values
}

i want to UPDATE Fax table and FaxData table that link between Fax&employees tables

i made viewmodel view to allow me to use the 2 model (Fax&Faxdata) in my view
but i have NULL value of SelectedEmployees in listboxfor
what is the problem in my viewmodel ??
namespace FaxProject.ViewModel
{
public class SendFaxVm
{
public List<SelectListItem> Employees { set; get; }
public int[] SelectedEmployees { set; get; }
public Fax fax { set; get; }
}
}
this is the view (Edite view)
#model FaxProject.ViewModel.SendFaxVm
#using (Html.BeginForm("Edit","Fax"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Fax</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.fax.Id)
<div class="form-group">
#Html.LabelFor(model => model.fax.Courier_Num, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.fax.Courier_Num, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.fax.Courier_Num, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.fax.NumberOfPages, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.fax.NumberOfPages, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.fax.NumberOfPages, "",
<div class="form-group">
#Html.LabelFor(model => model.Employees, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.ListBoxFor(a => a.SelectedEmployees, Model.Employees)
#Html.ValidationMessageFor(a => a.SelectedEmployees)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Back" class="btn btn-default" />
</div>
</div>
</div>
}
i got null exception in the listbox so how can i get the list of employees of edit it ??
public ActionResult Edit (int id,SendFaxVm vm,Fax fax)
{
var faxindb = Db.Faxes.Single(f=>f.Id==id);
faxindb.Courier_Num = fax.Courier_Num;
faxindb.NumberOfPages = fax.NumberOfPages;
faxindb.CompanyName = fax.CompanyName;
faxindb.ReceivingDate = fax.ReceivingDate;
faxindb.Attachment = fax.Attachment;
faxindb.Subject = fax.Subject;
Db.SaveChanges();
foreach (var userId in vm.SelectedEmployees)
{
var faxdataindb = Db.FaxDatas.Single(f => f.FaxId == fax.Id);
faxdataindb.EmpId = userId;
//to do : Save fd
Db.SaveChanges();
}
return RedirectToAction("FaxForm","Fax");
}
this is the action in Fax controller ...
Hey You can improve couple of things.
public class SendFaxVm
{
public List<SelectListItem> Employees { set; get; }
public List<int> SelectedEmployees { set; get; }
public Fax fax { set; get; }
}
Change array to a List in your Model, You can also use IEnumerable here too. If you , Also follow this example to populate Listbox https://www.codeproject.com/Articles/771999/ASP-Net-MVC-How-to-create-a-ListBox

Can't bind dropdownlist in a partial view in Code First Entity Framework

I'm new to MVC, so apologies for the noob question!
I have the following model (TimeEntry), used for entering timesheet details:
namespace MVCLogin.Models
{
public class TimeEntry
{
public int TimeEntryID { get; set; }
public int TaskTypeID { get; set; }
[ForeignKey("TaskTypeID")]
public virtual TaskType TaskType { get; set; }
public double MonHours { get; set; }
public double TueHours { get; set; }
public double WedHours { get; set; }
public double ThuHours { get; set; }
public double FriHours { get; set; }
}
}
Task type is based on the following model:
namespace MVCLogin.Models
{
public class TaskType
{
public int TaskTypeID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
I use the following partial view which is used for entering the timesheet details:
<div class="col-md-1">
#Html.EditorFor(model => model.TaskType)
#Html.ValidationMessageFor(model => model.TaskType)
</div>
<div class="col-sm-1">
#Html.TextBoxFor(model => model.MonHours , new { style = "width:100%", #class = "hours mon" })
#Html.ValidationMessageFor(model => model.MonHours)
</div>
<div class="col-sm-1">
#Html.TextBoxFor(model => model.TueHours, new { style = "width:100%", #class = "hours tue" })
#Html.ValidationMessageFor(model => model.TueHours)
</div>
<div class="col-sm-1">
#Html.TextBoxFor(model => model.WedHours, new { style = "width:100%", #class = "hours wed" })
#Html.ValidationMessageFor(model => model.WedHours)
</div>
<div class="col-sm-1">
#Html.TextBoxFor(model => model.ThuHours, new { style = "width:100%", #class = "hours thu" })
#Html.ValidationMessageFor(model => model.ThuHours)
</div>
<div class="col-sm-1">
#Html.TextBoxFor(model => model.FriHours, new { style = "width:100%", #class = "hours fri" })
#Html.ValidationMessageFor(model => model.FriHours)
</div>
I want the task type field to be a drop down, but I can't figure out how to wire it up. The classes and data are right, as if I scaffold a controller (using Visual Studio's Add Controller tool) for the TimeEntry class it works fine:
<div class="form-group">
#Html.LabelFor(model => model.TaskTypeID, "TaskTypeID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("TaskTypeID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.TaskTypeID, "", new { #class = "text-danger" })
</div>
</div>
How can I get the dropdownlist to work in the partial view?
The null parameter in your Html.DropDownList is the data of the Dropdown and it's a Ienumerable<SelectList>.
In order to pass the data. You need to transform your TaskType List into SelectListItem List (You can do this in the controller or somewhere else just before sending Model from Controller to View) and put it in your Model like this
namespace MVCLogin.Models
{
public class TimeEntry
{
public int TimeEntryID { get; set; }
public int TaskTypeID { get; set; }
[ForeignKey("TaskTypeID")]
public virtual TaskType TaskType { get; set; }
public IEnumerable<SelectListItem> TaskTypeSelectListItems { get; set; }
public double MonHours { get; set; }
public double TueHours { get; set; }
public double WedHours { get; set; }
public double ThuHours { get; set; }
public double FriHours { get; set; }
}
}
And the dropdown code will changed to
<div class="form-group">
#Html.LabelFor(model => model.TaskTypeID, "TaskTypeID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("TaskTypeID", model => model.TaskTypeSelectListItems, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.TaskTypeID, "", new { #class = "text-danger" })
</div>
</div>
Notice that the selected value will bind to "TaskTypeID" on your model when you post/get to the server.
Per your request: So the ViewBag is a property that enables you to dynamically share values from your controller to the view. Since it is dynamic, keep in mind that you will need to explicitly cast objects so the razor engine knows what they are at runtime. Note that upon reading about it (further reading at bottom), ViewBag is short lived and only lives within the current request so in the event that you have a model state error in your controller, you will need to reload the data into your viewbag.
This will help keep your models/view models very clean.
So on to the code, Test setup: I have my main page (Index)and I have a partial view (AddSomething) that is loaded in my main Index page. Given your models, here is what my controller looks like. Please Note that I am loading up the viewbag in my parent page initially but if an error occurs on my submit then I need to reload my viewbag since the dynamic object is short lived.
DALHelper dalHelp = new DALHelper();
public ActionResult Index()
{
//Get a list of task types from the data access layer
//Cast them into a SelectItemList and pass them into viewBag
ViewBag.TaskDD = dalHelp.GetTaskTypes().Select(a => new SelectListItem { Text = a.Name, Value = a.TaskTypeID.ToString() }).ToList();
return View();
}
public ActionResult AddSomething()
{
TimeEntry te = new TimeEntry();
return View(te);
}
[HttpPost]
public ActionResult AddSomething(TimeEntry te)
{
if (ModelState.IsValid)
{
//call my Data Access Layer to add new entry and redirect to wherver on success
if (dalHelp.CreateTimeEntry(te))
{
return RedirectToAction("Index");
}
else
{
//something happened during adding entry into DB, write whatever code to handle it gracefully
//I need to reload my viewbag since it has since been cleared (short lived)
ViewBag.TaskDD = dalHelp.GetTaskTypes().Select(a => new SelectListItem { Text = a.Name, Value = a.TaskTypeID.ToString() }).ToList();
ModelState.AddModelError("TimeEntryID", "An Error was encountered...");
return View(te);
}
}
else
{
//I need to reload my viewbag since it has since been cleared (short lived)
ViewBag.TaskDD = dalHelp.GetTaskTypes().Select(a => new SelectListItem { Text = a.Name, Value = a.TaskTypeID.ToString() }).ToList();
return View(te);
}
}
In my partial View I need to cast my viewbag item since it is a dynamic type so the razor engine can process: Also not that I am using Dropdownlistfor unstead of just dropdownlist:
#model deletemeweb2.Models.TimeEntry
#using (Html.BeginForm("AddSomething", "Home"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>TimeEntry</h4>
#Html.ValidationMessageFor(model => model.TimeEntryID, "", new { #class = "text-danger" })
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.TaskTypeID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.TaskTypeID, (IEnumerable<SelectListItem>)ViewBag.TaskDD, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.TaskTypeID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.MonHours, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.MonHours, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.MonHours, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TueHours, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TueHours, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TueHours, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.WedHours, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.WedHours, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.WedHours, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ThuHours, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ThuHours, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ThuHours, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FriHours, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.FriHours, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FriHours, "", 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>
}
Further reading about ViewBag and like properties when dealing with different controller needs: http://www.c-sharpcorner.com/blogs/viewdata-vs-viewbag-vs-tempdata-in-mvc1

How do I use two dropdown menu instances from the same model?

I am having an issue where I think I have set things up correctly, however the results are alway nil.
Here's the three models:
public class FamilyMember
{
[Key]
public int id { get; set; }
[Required]
[Display(Name = "First Name")]
public string firstName { get; set; }
[Required]
[Display(Name = "Surname")]
public string surname { get; set; }
[Required]
[Display(Name = "Date of Birth")]
public DateTime dob { get; set; }
public virtual FamilyRelationship FamilyRelationship { get; set; }
[Display(Name = "Full Name")]
public string fullName
{
get
{
return string.Format("{0} {1}", firstName, surname);
}
}
}
public class RelationshipType
{
[Key]
public int id { get; set; }
[Required]
[Display(Name="Relationship Type")]
public string relationshipType { get; set; }
public virtual FamilyRelationship FamilyRelationship { get; set; }
}
public class FamilyRelationship
{
[Key]
public int id { get; set; }
[ForeignKey("FamilyMembers")]
[Display(Name = "First Family Member")]
public int familyMemberPrimary { get; set; }
[ForeignKey("FamilyMembers")]
[Display(Name = "Second Family Member")]
public int familyMemberSecondary { get; set; }
[Display(Name = "Relationship Type")]
public int relationshipType { get; set; }
public virtual ICollection<FamilyMember> FamilyMembers { get; set; }
public virtual ICollection<RelationshipType> RelationshipTypes { get; set; }
}
So, I have successfully added data to FamilyMember and RelationshipType and the CRUD is working perfectly.
The problem is found in the Create Controller/View of FamilyRelationship. The dropdown works perfectly and shows the family members in the two associated menus and the relationship also shows on the relationType dropdown. However, when I click create all values are set to null.
Create Controller:
// GET: FamilyRelationships/Create
public ActionResult Create()
{
ViewBag.familyMember = new SelectList(db.FamilyMembers, "id", "fullName");
ViewBag.relationship = new SelectList(db.RelationshipTypes, "id", "relationshipType");
return View();
}
// POST: FamilyRelationships/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,familyMemberPrimary,familyMemberSecondary,relationshipType")] FamilyRelationship familyRelationship)
{
if (ModelState.IsValid)
{
db.FamilyRelationships.Add(familyRelationship);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(familyRelationship);
}
Create View:
#model FamilyTree.Models.FamilyRelationship
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>FamilyRelationship</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.familyMemberPrimary, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#*#Html.EditorFor(model => model.familyMemberPrimary, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.familyMemberPrimary, "", new { #class = "text-danger" })*#
#Html.DropDownList("familyMember", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.familyMemberPrimary, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.familyMemberSecondary, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("familyMember", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.familyMemberPrimary, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.relationshipType, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("relationship", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.relationshipType, "", 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>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Please let me know where am I going wrong and if possible provide an example to make this work.
#Html.DropDownList("familyMember"
you need to use the actual property names most likely
#Html.DropDownList("familyMemberPrimary"
you'd also have to rename the viewbag property to match for the items to show up.. or use dropdownlistfor
#Html.DropDownListFor(a => a.familyMemberPrimary, (SelectList)ViewBag.familyMember , new { #class = "form-control" })
you also need to add a dropdownlist for familyMemberSecondary
#Html.DropDownListFor(a => a.familyMemberSecondary, (SelectList)ViewBag.familyMember , new { #class = "form-control" })
This should get you pretty close..
#model FamilyTree.Models.FamilyRelationship
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>FamilyRelationship</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.familyMemberPrimary, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(a => a.familyMemberPrimary, (SelectList)ViewBag.familyMember, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.familyMemberPrimary, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.familyMemberSecondary, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(a => a.familyMemberSecondary, (SelectList)ViewBag.familyMember, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.familyMemberSecondary, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.relationshipType, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(a => a.relationshipType, (SelectList)ViewBag.relationship, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.relationshipType, "", 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>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
make sure you re set your ViewBag properties after a failed POST
DotNetFiddle Example
This is the expected behaviour. Remember Http is stateless. So you need to reload your dropdown data before returning to the view
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,familyMemberPrimary,familyMemberSecondary,
relationshipType")] FamilyRelationship familyRelationship)
{
if (ModelState.IsValid)
{
db.FamilyRelationships.Add(familyRelationship);
db.SaveChanges();
return RedirectToAction("Index");
}
//Let's reload the data for dropdown.
ViewBag.familyMember = new SelectList(db.FamilyMembers, "id", "fullName");
ViewBag.relationship = new SelectList(db.RelationshipTypes, "id", "relationshipType");
return View(familyRelationship);
}
EDIT: As per comment
The values within the FamilyRelationship so familyMemberPrimary,
familyMemberSecondary and relationshipType have values of 0, where I
was expecting the id's of each of these would be passed over.
Because you are using EditorFor helper method for familyMemberPrimary property in your view. So if you are not filling a value in that input field, it is going to have default value(0 for int type)
If you want that property to be filled with your dropdown selection(of family members), you should give the dropdown name value as familyMemberPrimary so that when you post the form, model binding will set the selected option value to familyMemberPrimary property.
#Html.DropDownList("familyMemberPrimary",
ViewBag.familyMember as IEnumerable<SelectListItem>,
htmlAttributes: new { #class = "form-control" })

Categories

Resources