Reputation: 1587
In my controller, I have a ViewModel containing 2 different models, called Drug and Food.
public class FoodDrugViewModel {
public IEnumerable<SGHealthDesktop.Models.Drug> Drugs { get; set; }
public IEnumerable<SGHealthDesktop.Models.Food> Foods { get; set; }
}
In my MainController, this is how I pass in the ViewModel into the Index.
// GET: Admin
public ActionResult Index() {
FoodDrugViewModel vm = new FoodDrugViewModel(); //initialize it
vm.Drugs = db.Drugs.ToList();
vm.Foods = db.Foods.ToList();
return View(vm);
}
In my view, I created two tables, and looped the items in each of the model, like this.
<table class="table" id="drugTable">
<thead>
<tr>
<th>Drug Name</th>
<th>Dosage</th>
<th>Unit</th>
<th>Type</th>
</tr>
</thead>
@foreach (var item in Model.Drugs) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.DrugName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Dosage)
</td>
<td>
@Html.DisplayFor(modelItem => item.Unit)
</td>
<td>
@Html.DisplayFor(modelItem => item.Type)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id = item.DrugId }) |
@Html.ActionLink("Details", "Details", new { id = item.DrugId }) |
@Html.ActionLink("Delete", "Delete", new { id = item.DrugId })
</td>
</tr>
}
</table>
@foreach (var item in Model.Foods) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.FoodName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Protein)
</td>
<td>
@Html.DisplayFor(modelItem => item.Carbohydrate)
</td>
<td>
@Html.DisplayFor(modelItem => item.TotalFat)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id = item.FoodId }) |
@Html.ActionLink("Details", "Details", new { id = item.FoodId }) |
@Html.ActionLink("Delete", "Delete", new { id = item.FoodId })
</td>
</tr>
}
To prevent both tables from showing up at the same time, I used a dropdownlist accompanied with JQuery so user can choose which table to see, and it's working as expected. However, my issue are as follow.
When I click on the "Details" ActionLink, or infact any of the 3 ActionLinks (Detail, Edit, Delete), I would like the relevant information to be displayed. For example, if I'm viewing the Drug table and if I click on "Details", the Detail view will display the drug information, and the same for Food.
However, I can't seem to figure out how that can be achieved. My Detail method is as follow, having Drug as the main model still. How can it detect if the user has chose to view the detail of Drug OR Food? As you can see in the code, it immediately finds the Drug details based on the id.
// GET: Admin/Details/5
public ActionResult Details(int? id) {
if (id == null) {
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Drug drug = db.Drugs.Find(id);
if (drug == null) {
return HttpNotFound();
}
return View(drug);
}
As for Create, there's no problem with it as I can again, allow a dropdownlist so user can choose what type, Drug or Food, they want to create and the form will be shown respectively (assuming in view I'm using the FoodDrugViewModel instead of Drug model). But how can i bind the data in the controller? By default, the Create method is as follow.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "DrugId,DrugName,Dosage,Unit,Type")] Drug drug) {
if (ModelState.IsValid) {
db.Drugs.Add(drug);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(drug);
}
Any help rendered will be much appreciated. Thanks in advance!
UPDATE: Issue on Create()
In the Create view, I declared the FoodDrugViewModel as follow
@model SGHealthDesktop.ViewModels.FoodDrugViewModel
And my Drug form looks like this (the same applies for Food).
<div id="drugDiv">
<div class="form-group">
@Html.LabelFor(model => model.Drug.DrugName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Drug.DrugName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Drug.DrugName, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Drug.Dosage, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Drug.Dosage, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Drug.Dosage, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Drug.Unit, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Drug.Unit, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Drug.Unit, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Drug.Type, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model => model.Drug.Type,
new SelectList(new[]
{ "Diabetic Medication", "Hypertension", "Kidney Disease", "Insulin", "High Cholesterol"
}) as SelectList, new { @class = "btn btn-default dropdown-toggle form-control" })
@Html.ValidationMessageFor(model => model.Drug.Type, "", new { @class = "text-danger" })
</div>
</div>
</div>
My Create() method is as follow
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "DrugName,Dosage,Unit,Type")] FoodDrugViewModel vm) {
try {
if (ModelState.IsValid) {
if (vm.Drug != null) {
db.Drugs.Add(vm.Drug);
}
db.SaveChanges();
return RedirectToAction("Index");
}
} catch (DataException dex) {
//Log the error (uncomment dex variable name and add a line here to write a log.
System.Diagnostics.Debug.WriteLine(dex);
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(vm.Drug);
}
I placed a breakpoint at the line the method is called, and "Drug" is null. May I know where did I went wrong? :(
Upvotes: 0
Views: 1050
Reputation: 963
I can suggest you two ways to do this.
Food_Edit, Food_Details, Food_Delete, Drug_Edit, Drug_Details, Drug_Delete
@Html.ActionLink("Edit", "Edit", new { id = item.FoodId, type="Food" })
@Html.ActionLink("Edit", "Edit", new { id = item.DrugId, type="Drug" })
Upvotes: 1
Reputation: 29683
You can pass one more parameter to your ActionResult
to differentiate between Drug
and Food
. For an example, I would be adding type
param, with value drug
and food
for respective actions.
Drug
@foreach (var item in Model.Drugs) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.DrugName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Dosage)
</td>
<td>
@Html.DisplayFor(modelItem => item.Unit)
</td>
<td>
@Html.DisplayFor(modelItem => item.Type)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id = item.DrugId, type="drug" }) | @Html.ActionLink("Details", "Details", new { id = item.DrugId, type="drug" }) | @Html.ActionLink("Delete", "Delete", new { id = item.DrugId, type="drug" })
</td>
</tr>
}
Food
@foreach (var item in Model.Foods) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.FoodName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Protein)
</td>
<td>
@Html.DisplayFor(modelItem => item.Carbohydrate)
</td>
<td>
@Html.DisplayFor(modelItem => item.TotalFat)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id = item.FoodId, type="food" }) | @Html.ActionLink("Details", "Details", new { id = item.FoodId, type="food" }) | @Html.ActionLink("Delete", "Delete", new { id = item.FoodId, type="food" })
</td>
</tr>
}
Your ActionResult Details
now should be accepting two arguments, id
and type
.
// GET: Admin/Details/5
public ActionResult Details(int? id, string type) {
//You do not want to do anything if you don't have type value too, so the condition
if (id == null || string.IsNullOrEmpty(type)) {
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
if(type=="drug"){
Drug drug = db.Drugs.Find(id);
if (drug == null) {
return HttpNotFound();
}
return View(drug);
}
else
{
Food food = db.Foods.Find(id);
if (food == null) {
return HttpNotFound();
}
return View(food);
}
}
Hope you will be handling your view efficiently with different models
passed.
EDIT
You might also check it in below way, by adding ternary operation, but am not sure whether it will work or not. You can give a try.
// GET: Admin/Details/5
public ActionResult Details(int? id, string type) {
//You do not want to do anything if you don't have type value too, so the condition
if (id == null || string.IsNullOrEmpty(type)) {
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var model=type=="drug"?db.Drugs.Find(id):db.Foods.Find('id');
if (model == null) {
return HttpNotFound();
}
return View(model);
}
Upvotes: 2