.NET 4.51, MVC 5
I have a view Edit.cshtml as follows:
#model EnergyMission.Country.Dtos.GetCountryOutput
#{
ViewBag.Title = "Edit Country";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#Html.Partial("_AddEditPartial", new ViewDataDictionary { { "ActionName", "Edit" } })
and then New.cshtml as follows:
#model EnergyMission.Country.Dtos.GetNewCountryOutput
#{
ViewBag.Title = "New Country";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#Html.Partial("_AddEditPartial", new ViewDataDictionary { { "ActionName", "Create" } })
Note from the above that:
Each one has a different model namely GetCountryOutput and GetNewCountryOutput. Both of these have the same properties. Currently they cannot be made the same class for other reasons.
I pass the action name so that I can use it in the Html.BeginForm in the partial view
I still need to figure out how to pass the model type to the partial view
The _AddEditPartial.cshtml looks as follows:
#model EnergyMission.Country.Dtos.GetCountryOutput
<h3>#ViewData["ActionName"]</h3>
#using (Html.BeginForm(#ViewData["ActionName"], "Countries"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>CountryTableModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.AbbreviationCode, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.AbbreviationCode, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.AbbreviationCode, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.InternationalCountryDialCode, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.InternationalCountryDialCode, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.InternationalCountryDialCode, "", new { #class = "text-danger" })
</div>
</div>
<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>
}
My questions are as follows:
Is there an easier way to share a common data entry layout for Add / Edit operations that whay I am doing above?
If I were to follow the approach described how do I:
Pass the model type to the partial view. Is it something like:
_
#Html.Partial("_AddEditPartial", new ViewDataDictionary { { "ActionName", "Create" }, {"ModelTypeName", "EnergyMission.Country.Dtos.GetCountryOutput"} })
and then in _AddEdit.cshtml
_
#model typeof(#ViewData["ModelTypeName"])
Why is the following not valid:
_
#using (Html.BeginForm(ViewData["ActionName"], "Countries"))
as that gives me a compile time error.
Create a single view, then use your controller to direct to a single view and use your repository to insert/update accordingly in a Save method. However, please note, in the example below you will need to make sure the model is the same - based on the principle that you should remove duplication. If that's not possible consider using a dynamic model in your view or using the ViewModel you can map the properties accordingly (anyway I don't want to over complicate the answer as I'm hoping this provides the solution you need - give or take some refactoring here and there).
A simplified example of the controller and repository (using a viewmodel)....
public class ProductController : Controller
{
private IProductRepository _repository;
public ProductController(IProductRepository productsRepository)
{
this._repository = productsRepository;
}
public ViewResult Edit(int productID)
{
ProductEditViewModel model = new ProductEditViewModel
{
Product = this._repository.Products.Where(m => m.ProductID == productID).FirstOrDefault()
};
return View(model);
}
[HttpPost]
public ActionResult Edit(int productId)
{
ProductEditViewModel model = new ProductEditViewModel
{
Product = _repository.Products.First(m => m.ProductID == productId)
};
TryUpdateModel(model);
if (ModelState.IsValid)
{
_repository.Save(model.Product);
return RedirectToAction("Index");
}
else
{
return View(model);
}
}
public ViewResult Create()
{
return View("Edit", new Product());
}
[HttpPost]
public ActionResult Create(Product product)
{
return Edit(product.ProductID);
}
}
The repository....
public class SqlProductRepository : IProductRepository
{
private MyDataContext db;
private Table<Product> _productsTable;
public IQueryable<Product> Products
{
get { return _productsTable; }
}
public void Save(Product product)
{
if (product.ProductID == 0)
{
_productsTable.InsertOnSubmit(product);
}
else if (_productsTable.GetOriginalEntityState(product) == null)
{
_productsTable.Attach(product);
_productsTable.Context.Refresh(RefreshMode.KeepCurrentValues, product);
}
_productsTable.Context.SubmitChanges();
}
}
Well the answer to 3. is that html.beginform expects a string as its first parameter, and as you are using ViewData which is a
Dictionary<string, object>
you need to cast your value as a string:
#using (Html.BeginForm(ViewData["ActionName"] as string, "Countries"))
For 1. You could try deriving both of your dto classes from a base class and setting that class as the model type for both the add and edit views
Related
This question already has answers here:
Populating a razor dropdownlist from a List<object> in MVC
(9 answers)
Closed 3 years ago.
I have a view to create an 'Appointment' after choosing some options in 3 different drop-down menus (Patient, Doctor, Clinic)
I need help with creating and populating these 3 drop-down menus.
I'm pretty new to ASP.NET MVC and C#. So, your help is most appreciated.
I'll include the appointment controller and appointment creation view code.
AppointmentController
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using ClinicManagement.Models;
namespace ClinicManagement.Controllers
{
public class AppointmentController : Controller
{
// GET: Appointment
public ActionResult Index()
{
using (HospitalDatabaseEntities DataBase = new HospitalDatabaseEntities())
{
return View(DataBase.Appointments.ToList());
}
}
// GET: Appointment/Details/5
public ActionResult Details(int id)
{
using (HospitalDatabaseEntities DataBase = new HospitalDatabaseEntities())
{
return View(DataBase.Appointments.Where(x => x.AppintID == id).FirstOrDefault());
}
}
// GET: Appointment/Create
public ActionResult Create()
{
return View();
}
// POST: Appointment/Create
[HttpPost]
public ActionResult Create(Appointment appointment)
{
try
{
using (HospitalDatabaseEntities DataBase = new HospitalDatabaseEntities())
{
DataBase.Appointments.Add(appointment);
DataBase.SaveChanges();
}
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
// GET: Appointment/Edit/5
public ActionResult Edit(int id)
{
using (HospitalDatabaseEntities DataBase = new HospitalDatabaseEntities())
{
return View(DataBase.Appointments.Where(x => x.AppintID == id).FirstOrDefault());
}
}
// POST: Appointment/Edit/5
[HttpPost]
public ActionResult Edit(int id, Appointment appointment)
{
try
{
using (HospitalDatabaseEntities DataBase = new HospitalDatabaseEntities())
{
DataBase.Entry(appointment).State = EntityState.Modified;
DataBase.SaveChanges();
}
// TODO: Add update logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
// GET: Appointment/Delete/5
public ActionResult Delete(int id)
{
using (HospitalDatabaseEntities DataBase = new HospitalDatabaseEntities())
{
return View(DataBase.Appointments.Where(x => x.AppintID == id).FirstOrDefault());
}
}
// POST: Appointment/Delete/5
[HttpPost]
public ActionResult Delete(int id, FormCollection collection)
{
try
{
// TODO: Add delete logic here
using (HospitalDatabaseEntities DataBase = new HospitalDatabaseEntities())
{
Appointment appointment = (DataBase.Appointments.Where(x => x.AppintID == id).FirstOrDefault());
DataBase.Appointments.Remove(appointment);
DataBase.SaveChanges();
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
}
Appointment 'Create' View
#model ClinicManagement.Models.Appointment
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Appointment</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.DoctorID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DoctorID, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.DoctorID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PatientID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PatientID, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PatientID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ClinicID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ClinicID, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ClinicID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Date, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Date, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Date, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create Appointment" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
If the drop down menu options are in a database, why not add a list to your model, populate that list in your GET ActionMethod and then render it using the DropdownListFor helper tag.
For example...
public class Appointment
{
public IEnumerable<SelectListItem> Clinic {get; set;}
//You should add this for every dropdown menu you intend to put in the list.
//I am guessing you already have a model like this as this was not in the question
}
public class Clinic
{
public int ClinicId {get; set;}
public string ClinicName {get; set;}
}
In the controller, you can then query the database for the options
public ActionResult Create()
{
var Clinic = context.Clinic.ToList();
var model = new Appointments()
{
Clinic = Clinic.Select(x => new SelectListItem
{
Value = x.ClinicId.ToString(),
Text = x.ClinicName
}
}
return View(model);
}
Like before, you would have to do this for all the fields. If you are worried about the numerous roundtrip to the database to get the values, do some research about Z.EntityFrameWork nuget pakages that lets you run batch SQL statements so you can get all three results with one database round trip.
Then in the view, you can do this...
#Html.DropDownListFor(m => m.ClinicId, Model.Clinic, "Select Clinic", new { #class = "form-control", id = "clinic" })
In Create controller GET , you should create 3 viewbag like
ViewBag.Patient = Database.Patient.ToList();
...
and in view, use dropdownlist:
#Html.DropDownList("PatientId", new SelectList(ViewBag.Accounts, "PatientId", "PatientName")), "choose the patient", new { #class = "form-control" }))
This question already has answers here:
MVC5 - How to set "selectedValue" in DropDownListFor Html helper
(5 answers)
Closed 4 years ago.
I'm quite new to ASP.Net and I'm trying to make a reservation to a specified branch. When I click the reservation link of a branch, it comes to a drop-down list for me to choose a branch from those already existed in the database. What I want is the branch has already been set based on the reservation link of that branch I clicked. In the database, the reservation entity has an attribute referencing to the branchId in the branch entity. I have viewed some related questions such as MVC5 - How to set “selectedValue” in DropDownListFor Html helper, but neither solved my question properly. I still have no idea of this for now.
The following images are what it looks like now.
This is the create action in my reservation controller:
// GET: Reservations/Create
public ActionResult Create()
{
ViewBag.branchId = new SelectList(db.Branches, "branchId", "name");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "reservationId,branchId,customerId,date")] Reservation reservation)
{
reservation.customerId = User.Identity.GetUserId();
ModelState.Clear();
TryValidateModel(reservation);
if (ModelState.IsValid)
{
db.Reservations.Add(reservation);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.branchId = new SelectList(db.Branches, "branchId", "name", reservation.branchId);
return View(reservation);
}
This is my reservation create view:
#model Mel_Medicare_Location_Reservation_System.Models.Reservation
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Reservation</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group ">
#Html.LabelFor(model => model.branchId, "branchId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("branchId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.branchId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.customerId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.customerId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.customerId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.date, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.date, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.date, "", 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")
}
You should have the dropdown options as a get-only property of your model, then use the #Html.DropDownListFor() HTML helper to bind model.branchId to that list, which will set the value appropriately.
I would re-write your Create() controller method to return the view with an instance of the Reservation view model bound to it. This will involve adding a constructor to the view model class which accepts a branch id as a parameter. Something like:
// GET: Reservations/Create
public ActionResult Create(int branchId)
{
return View(new Reservation(branchId));
}
And in your Reservation model:
public class Reservation
{
public int BranchId { get; set; }
// Other properties...
public SelectList BranchOptions
{
get
{
return new SelectList(db.Branches);
}
}
public Reservation(int branchId)
{
this.BranchId = branchId;
}
}
Then in your view:
#Html.DropDownListFor(m => m.BranchId, Model.BranchOptions, htmlAttributes: new { #class = "form-control" })
The error message I get:
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /Member/EditMember
My Actionlink that calls the Edit method looks like this:
#Html.ActionLink("Rediger", "EditMember", new { item.MemberID })
And the view that represents the EditMember page:
#using (Html.BeginForm("EditMember", "Member", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.MemberID)
<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, "Navn skal angives", 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
...
}
And the controller action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditMember(Member model)
{
Member members = new Member();
var member = DBContext.Members.Find(model.MemberID);
if (member == null)
{
return HttpNotFound();
}
if(ModelState.IsValid)
{
DBContext.SaveChanges();
}
return View(member);
}
I have also tried to change the action methods parameter so it looks like this
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditMember(int? id) { ... }
But the error remains the same.
HOWEVER...
If I remove the [HttpPost annotation, then the error dissappears, and it will find the requested URL. But it just won't submit the changes and save them to the database.
That is, if I do:
[ValidateAntiForgeryToken]
public ActionResult EditMember(Member model)
Then it finds the requested URL, but doesn't save the changes to the database.
What might be my problem?
I suspect you are not passing the Id in the HttpGet method. This could be as a result of using a ViewModel to display member info.
There is no row in the table with the specified member Id.
In your controller you should have two actions (HttpGet and HttpPost)
[HttpGet]
public ActionResult EditMember(int id)
{
var member = db.member..SingleOrDefault(x => x.Id == id);
if (member != null)
{
var memberViewModel = new MemberViewModel();
memberViewModel.MemberID = member.Id;
memberViewModel.FirstName = member.FirstName;
memberViewModel.LastName = member.LastName;
return View(memberViewModel);
}
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditMember(Member model)
{
Member members = new Member();
var member = DBContext.Members.Find(model.MemberID);
if (member == null)
{
return HttpNotFound();
}
if(ModelState.IsValid)
{
DBContext.SaveChanges();
}
return View(member);
}
I don't know if you can understand my question but I am trying to making this clear.
For now, I have some methods in my controller. And When I click Edit, it supposed to show the original things like the following. But instead of that, I got Nothing in the text boxes. I hope someone could me some advice, thanks!
Here is my Edit Method in the controller.
[HttpGet]
public ActionResult Edit(string programid)
{
Program program = db.Programs.Find(programid);
return View(program);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ProgramID,SerialNo,ProgramName,SubmissionDeadline,LevelID,DepartmentID,HierachyL1,HierachyL2,R1,R2,R3,R4,R5,AwardAmount,DateReleased,DatePosted,DateOpen,InternalDueDate,Area1,Area2,Area3,Weblink,Word,AgencyID")]Program program)
{
try
{
if (ModelState.IsValid)
{
db.Entry(program).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("All");
}
return View(program);
}
catch(DataException)
{
ModelState.AddModelError("", "Unable to save changes. Try again");
PopulateDepartmentDropDownList(program.DepartmentID);
}
return View(program);
}
For the Model, I just have some variables.
[Table("[RP_Program]")]
public class Program
{
[Key]
public string ProgramID { get; set; }
public string SerialNo { get; set; }
public string ProgramName { get; set; }
public DateTime? SubmissionDeadline { get; set; }
public int AgencyID { get; set; }
}
For the view, Here is part of it, most codes are the same.
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Program</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.ProgramID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProgramID, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProgramID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SerialNo, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.SerialNo, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SerialNo, "", new { #class = "text-danger" })
</div>
</div>
What you need to fix is changing argument name to match controller action method argument in ActionLink helper:
View
#Html.ActionLink("Edit", "Edit", "ControllerName", new { programid = item.ProgramID })
Controller
public ActionResult Edit(string programid)
{
Program program = db.Programs.Find(programid);
return View(program);
}
The previous setup tries to pass the value into id argument which doesn't actually exist in Edit action method:
// incorrect
#Html.ActionLink("Edit", "Edit", new { id = item.ProgramID })
The problem is the parameter names don't match. ASP.NET matches the names to see which parameter gets which value. So in using:
#Html.ActionLink("Edit", "Edit", new { id = item.ProgramID })
You are sending a parameter with name id, but your Edit routine is looking for a parameter called programid:
public ActionResult Edit(string programid)
You need to change one of the parameter names so that they match.
I'm trying to make a razor view that can post a model to my controller.
I've added a dropdown, however platform is always null when i post to my controller.
What am i doing wrong?
This is my view
#using (Html.BeginForm("Register", "Home", FormMethod.Post, new { #class = "form-horizontal", #id = "form" }))
{
#{
var platformList = new List<SelectListItem>() {
new SelectListItem(){ Value="I", Text="iOS"},
new SelectListItem(){ Value="A", Text="Android"},
};
}
<div class="form-group">
#Html.LabelFor(model => model.platform, "Plattform", new { #for = "inputPlatform", #class = "col-lg-3 control-label" })
<div class="col-lg-9">
#Html.DropDownListFor(model => model.platform, platformList, new { #class = "form-control", #id = "inputPlatform" })
</div>
</div>
}
This is my model
public class RegistrationModel
{
public String platform { get; set; }
}
My Controller
[HttpPost]
[AllowAnonymous]
public ActionResult Register(RegistrationModel RegistrationModelViewModel)
{
}
I couldn't get your view to work. There appears to be a formatting issue with the drop down declaration. It has an extra comma and was missing a end }. I kept getting a parse error, which is odd as you say you can get the post to work.
Anyway, I've created an example below which works and so I hope is of some use.
Model
public class RegistrationModel
{
public string platform { get; set; }
}
View
#model TestMVC.Models.RegistrationModel
#using (Html.BeginForm("Register", "Register", FormMethod.Post, new { #class = "form-horizontal", #id = "form" }))
{
var platformList = new List<SelectListItem>() {new SelectListItem(){ Value="I", Text="iOS"}, new SelectListItem(){ Value="A", Text="Android"}};
<div class="form-group">
#Html.LabelFor(model => model.platform, "Plattform", new {#for = "inputPlatform", #class = "col-lg-3 control-label"})
<div class="col-lg-9">
#Html.DropDownListFor(model => model.platform, platformList, new {#class = "form-control", #id = "inputPlatform"})
</div>
</div>
<button type="submit"> Submit</button>
}
Controller
public class RegisterController : Controller
{
[HttpGet]
public ActionResult Register()
{
return View();
}
[HttpPost]
public ActionResult Register(RegistrationModel model)
{
//Do something here;
}
}