Access common view for different models with same columns - c#

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.

Related

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

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" })

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