In my viewModel I have:
public class PersonViewModel
{
public Person Person { get; set; }
public int SelectRegionId { get; set; }
public IEnumerable<SelectListItem> Regions { get; set; }
}
But what do I have to do in my Controller/View to show the values? What I have now:
Controller:
public ActionResult Create()
{
var model = new ReUzze.Models.PersonViewModel
{
Person = new Person(),
Regions = new SelectList(this.UnitOfWork.RegionRepository.Get(), "Id", "Name")
};
return View(model);
}
View:
<div class="form-group">
#Html.LabelFor(model => model.Person.Address.Region)
#Html.DropDownListFor(model => model.SelectRegionId, new SelectList(Model.Regions, "Id", "Name"), "Choose... ")
</div>
But it gives an error like this:
Cannot implicitly convert type 'System.Web.Mvc.SelectList' to 'System.Collections.Generic.IEnumerable<System.Web.WebPages.Html.SelectListItem>'. An explicit conversion exists (are you missing a cast?)
Your ViewModel has a property of type 'IEnumerable', but SelectList does not satisfy that type.
Change your code like this:
public class PersonViewModel
{
public Person Person { get; set; }
public int SelectRegionId { get; set; }
public SelectList Regions { get; set; }
}
View:
<div class="form-group">
#Html.LabelFor(model => model.Person.Address.Region)
#Html.DropDownListFor(model => model.SelectRegionId, Model.Regions, "Choose... ")
</div>
You are creating SelectList instance twice. Get rid of one of them:
#Html.DropDownListFor(model => model.SelectRegionId, Model.Regions, "Choose... ")
Related
I have these models and a view model. When form posting to create the entries, the selected value for WardID and DoctorID becomes zero in the controllers POST method.
View code
#model NewClient.HospitalMgt.ViewModel.PatientAdmissionViewModel
....
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => model.Admission.WardID, "Ward")
</div>
<div class="editor-field">
#Html.DropDownList("WardID", String.Empty)
#Html.ValidationMessageFor(model => model.Admission.WardID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Admission.DoctorID, "Doctor")
</div>
<div class="editor-field">
#Html.DropDownList("DoctorID", String.Empty)
#Html.ValidationMessageFor(model => model.Admission.DoctorID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Patient.PatientName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Patient.PatientName)
#Html.ValidationMessageFor(model => model.Patient.PatientName)
</div>
.... //more controls for properties of Patient
<div class="editor-label">
#Html.LabelFor(model => model.Admission.AdmissionNumber)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Admission.AdmissionNumber)
#Html.ValidationMessageFor(model => model.Admission.AdmissionNumber)
</div>
.... //more controls for properties of Admission
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Controller code
public ActionResult Create()
{
ViewBag.WardID = new SelectList(db.Wards, "WardID", "WardNumber");
ViewBag.DoctorID = new SelectList(db.Doctors, "DoctorID", "DoctorName");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(PatientAdmissionViewModel Id)
{
if (ModelState.IsValid)
{
var c = new Patient()
{
Address = Id.Patient.Address,
ContactNumber = Id.Patient.ContactNumber,
EmergencyContact = Id.Patient.EmergencyContact,
PatientName = Id.Patient.PatientName,
};
var a = new Admission()
{
AdmissionNumber = Id.Admission.AdmissionNumber,
AdmissionDate = Id.Admission.AdmissionDate,
**WardID = Id.Admission.WardID, //becomes zero
DoctorID = Id.Admission.DoctorID //becomes zero**
};
db.Patients.Add(c);
db.Admissions.Add(a);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.WardID = new SelectList(db.Wards, "WardID", "WardNumber", Id.Admission.WardID);
ViewBag.DoctorID = new SelectList(db.Doctors, "DoctorID", "DoctorName", Id.Admission.DoctorID);
return View(Id);
}
Models and View models
//[NotMapped]
public class PatientAdmissionViewModel
{
public Patient Patient { get; set; }
public Admission Admission { get; set; }
}
public class Patient
{
public int PatientID { get; set; }
[Required(ErrorMessage = "A Patient Name is required")]
public string PatientName { get; set; }
public string Address { get; set; }
[Required(ErrorMessage = "A Contact Number is required")]
public string ContactNumber { get; set; }
public string EmergencyContact { get; set; }
public virtual Ward Ward { get; set; }
}
public class Admission
{
public int AdmissionID { get; set; }
public string AdmissionNumber { get; set; }
public Nullable<int> PatientID { get; set; }
public int WardID { get; set; }
public int DoctorID { get; set; }
public System.DateTime AdmissionDate { get; set; }
public virtual Ward Ward { get; set; }
public virtual ICollection<PatientPayment> PatientPayments { get; set; }
}
You use of #Html.DropDownList("WardID", String.Empty) and #Html.DropDownList("DoctorID", String.Empty) are generating inputs with name="WardID" and name="DoctorID" respectively, but you model does not contain properties with those names (it does however contain a property named Admission which contains those properties).
You 'view model' is not really a view model (a view model should not contain properties which are data models when editing). Your view model should also contain properties for the SelectList's so that you can strongly bind to your model. Your view model needs to be
public class PatientAdmissionViewModel
{
[Display(Name = "Ward"]
[Required(ErrorMessage = "Please select a ward")]
public int? SelectedWard { get; set; }
[Display(Name = "Doctor"]
[Required(ErrorMessage = "Please select a doctor")]
public IEnumerable<SelectListItem> WardList { get; set; }
public int? SelectedDoctor { get; set; }
public IEnumerable<SelectListItem> DoctorList { get; set; }
public string PatientName { get; set; }
... // other properties of Patient that you need in the view
public string AdmissionNumber{ get; set; }
... // other properties of Admission that you need in the view
}
and in the GET method, initialize an instance of the view model, set its properties and return it to the view
public ActionResult Create()
{
var model = new PatientAdmissionViewModel()
{
WardList = new SelectList(db.Wards, "WardID", "WardNumber"),
DoctorList = new SelectList(db.Doctors, "DoctorID")
};
return View(model);
}
and in the view
#Html.DropDownListFor(m => m.SelectedWard, Model.WardList, "-Please select-")
....
#Html.DropDownListFor(m => m.SelectedDoctor, Model.DoctorList, "-Please select-")
and finally, in the POST method, you initialize new instances of your data models, set their properties based on the values in the view models and save the data models.
I also recommend you read the answers to What is ViewModel in MVC?.
instead of this
#Html.DropDownList("WardID", String.Empty)
#Html.DropDownList("DoctorID", String.Empty)
use this on view
#Html.DropDownListFor(model => model.Admission.WardID, (SelectList)ViewBag.WardID, "--Select One--")
#Html.DropDownListFor(model => model.Admission.DoctorID, (SelectList)ViewBag.DoctorID, "--Select One--")
use this on controller
WardID = Id.Admission.WardID,
DoctorID = Id.Admission.DoctorID
I'm working with EF and MVC, new stuff to me.
I have the following class:
public class Client
{
[Key]
public int ClientId { get; set; }
public Category Category { get; set; }
}
and I want to show in the View related to the "Create" ActionResult, a selectable DropDownList with all the categories to choose one.
I have a ViewModel also:
public class CategoryViewModel
{
public int SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
which I use in the controller:
private IEnumerable<SelectListItem> GetCategories()
{
var db = new MyDBContext();
var categories = db.Categories
.Select(x =>
new Category
{
CategoryId = x.CategoryId,
Name = x.Name
});
return new SelectList(categories, "Value", "Text");
}
...and in the Create():
public ActionResult Create()
{
var model = new CategoryViewModel();
model.Categories = GetCategories();
return View(model);
}
I dont know how to populate the dropdown in the view:
<div class="form-group">
#Html.LabelFor(model => model.Category, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(WHAT GOES HERE????)
</div>
</div>
Thanks for rescuing me! (RE5 quote)
There are different overloads but this is an option:
#Html.DropDownListFor(x => x.SelectedCategoryId, Model.Categories)
Can't figure out what I'm doing wrong. When the form in the view is posted the model properties turn out to be null.
Model
public class RegistrationModel
{
public RegistrationModel()
{
Registration = new REGISTRATION();
AddPayment = true;
}
public REGISTRATION Registration { get; set; }
public bool AddPayment { get; set; }
}
View
#model Client.Models.RegistrationModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(excludePropertyErrors: false)
<div class="form-group">
#Html.DropDownList("SECTION_ID", null, string.Empty, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.DropDownList("STUDENT_ID", null, string.Empty, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.DropDownList("STATUS_ID", null, string.Empty, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.CheckBoxFor(model => model.AddPayment)
</div>
<p>
<input type="submit" class="btn btn-success" value="Create" />
</p>
}
Controller
public ActionResult Create()
{
//code to populate view dropdowns
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(RegistrationModel model)
{
WriteFileLog(_logPath, Newtonsoft.Json.JsonConvert.SerializeObject(model));
}
In the controller's Create action that handles the post model properties are null.
Registration Class (autogenerated by EF from database):
public partial class REGISTRATION
{
public REGISTRATION()
{
this.REGISTRATION_AUDIT = new HashSet<REGISTRATION_AUDIT>();
}
public int ID { get; set; }
public int SECTION_ID { get; set; }
public int STUDENT_ID { get; set; }
public int STATUS_ID { get; set; }
public virtual ICollection<REGISTRATION_AUDIT> REGISTRATION_AUDIT { get; set; }
public virtual SECTION SECTION { get; set; }
public virtual V_REGISTRATION_STATUS V_REGISTRATION_STATUS { get; set; }
public virtual PERSON PERSON { get; set; }
}
I would recommend using the strongly-typed helpers, like so:
#Html.DropDownListFor(m => m.Registration.SECTION_ID, null, string.Empty, new { #class = "form-control" })
Otherwise, you need to adjust the names you're using to
#Html.DropDownList("Registration.SECTION_ID", null, string.Empty, new { #class = "form-control" })
You can probably simplify what you're doing by duplicating the Registration class's members into your view model, replacing the Registration property.
As #StephenMuecke points out, you're missing a few parts from your model/markup. The template for the DropDownList helper you're using is
DropDownListFor(
[model property to bind],
[collection of possible values to bind],
[option label],
[HTML attributes])
Passing null for that second parameter means you have no values to populate the generated <select> element with, and should normally generate an exception.
I'm not a fan of using ViewBag to pass collections into the view, so I'd recommend something like
public class RegistrationModel
{
public RegistrationModel()
{
Registration = new REGISTRATION();
AddPayment = true;
}
public REGISTRATION Registration { get; set; }
public bool AddPayment { get; set; }
public SelectList Sections { get; set; }
public SelectList Students { get; set; }
public SelectList Statuses { get; set; }
}
and then adjust the markup accordingly:
#Html.DropDownListFor(m => m.Registration.SECTION_ID, Model.Sections, string.Empty, new { #class = "form-control" })
I am trying to bind a dropdownlistfor to a list that comes from the database
In the model
public class Actor
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
...
// I have a database table called genre
[ForeignKey("Genre")]
public virtual IEnumerable<Genre> Genres { get; set; }
}
In the controller
// GET: Actors/Create
public ActionResult Create()
{
Actor actor = new Actor();
actor.Genres = new // Believe I need to do something here...
return View(actor);
}
In the view
<div class="form-group">
#Html.LabelFor(model => model.Genres, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="dropdown">
#Html.DropDownListFor(M => M.Genre, new SelectList(Model.Genres, "Value", "Text"))
</div>
</div>
</div>
What is it i need to do?
Thanks
I would usually add a property to my ViewModel that is of type IEnumerable<SelectListItem>:
public class ActorViewModel
{
public int GenreId { get; set; }
public IEnumerable<SelectListItem> Genres { get; set; }
}
Then in my Controller I would add a method that will return these for you:
public IEnumerable<SelectListItem> GetGenres()
{
foreach (var genre in dbContext.Genres)
{
yield return new SelectListItem
{
Value = genre.Id.ToString(),
Text = genre.Name
};
}
}
Then set as so:
viewModel.Genres = this.GetGenres();
Then in my View
#Html.DropDownListFor(m => m.GenreId, Model.Genres, "Please select")
You should create ViewModel for this
public class ActorViewModel
{
public Actor actor { get; set; }
public SelectList Genres { get; set; }
public int GenreId { get; set; }
}
Create a method that returns the genres as a selectlist
public SelectList GetAsSelectList()
{
var genres = (from s in GetAllGenres()
select new
{
s.Id, s.Name
}).ToList();
return new SelectList((IEnumerable)genres, "Id", "Name");
}
In your controller you populate the model properties:
var model = new ActorViewModel
{
actor = new Actor(),
Genres = GetAsSelectList(),
GenreId = 0
};
return View(model);
In the view:
#Html.DropDownListFor(M => M.GenreId, Model.Genres,"--Select--")
My main entity is the Recipe which contains a collection of Ingredient items as follows:
public class Recipe {
[Key]
public virtual int RecipeId { get; set; }
public string RecipeName { get; set; }
...
public virtual ApplicationUser LastModifiedBy { get; set; }
public virtual IList<Ingredient> Ingredients { get; set; }
}
public class Ingredient {
public virtual int IngredientId { get; set; }
[Display(Name = "Name")]
public string IngredientName { get; set; }
....
public virtual IList<Recipe> Recipes { get; set; }
}
Which is fine. Then my controller and view for creating a new Recipe are as follows:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "stuff to include")] Recipe recipe)
{
IList<int> ingredientIds = (ModelState.Values.ElementAt(1).Value.AttemptedValue).Split(',').Select(int.Parse).ToList(); //[1,2,3,4,5]
foreach (int id in ingredientIds) {
Ingredient ing = db.Ingredients.Where(i => i.IngredientId == id).FirstOrDefault() as Ingredient;
recipe.Ingredients.Add(ing);
}
db.Recipes.Add(recipe);
db.SaveChanges();
return RedirectToAction("Index");
ViewBag.Ingredients = new MultiSelectList(db.Ingredients,
"IngredientId", "IngredientName", string.Empty);
ViewBag.CreatedById = new SelectList(db.Users, "Id", "Email", recipe.CreatedById);
return View(recipe);
}
And the view:
#for (Int16 i = 0; i < 5; i++) {
<div class="form-group">
#Html.LabelFor(model => model.Ingredients, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("Ingredients", null, htmlAttributes: new { #class = "form-control" })
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="button" value="Add Ingredients" class="btn btn-default" />
</div>
</div>
So this sets ModelState.Values.ElementAt(1).Value.AttemptedValue = "1,3,5,4,5" where this is a list of id numbers. I know I can come in before the if (ModelState.IsValid) and iterate through the above and place it into recipe.Ingredients which is fine except...
It feels just so un ASP.NET MVC like, as if there's no way they could have thought of so much and not thought of this scenario? Am I missing something here? The ingredients list will be too long to make a multi select list any use.
You are creating arbitrary dropdownlists that all have the same id (invalid html) and name attribute that has no relationship to your model and wont bind on post back. You first need to create view models that represent what you want to display.
public class RecipeVM
{
[Required]
public string Name { get; set; }
[Display(Name = Ingredient)]
[Required]
public List<int?> SelectedIngredients { get; set; }
public SelectList IngredientList { get; set; }
}
Then in the controller
public ActionResult Create()
{
RecipeVM model = new RecipeVM();
// add 5 'null' ingredients for binding
model.SelectedIngredients = new List<int?>() { null, null, null, null, null };
ConfigureViewModel(model);
return View(model);
}
[HttpPost]
public ActionResult Create(RecipeVM model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model);
return View(model);
}
// Initialize new instance of your data model
// Map properties from view model to data model
// Add values for user, create date etc
// Save and redirect
}
private void ConfigureViewModel(RecipeVM model)
{
model.IngredientList = new SelectList(db.Ingredients, "IngredientId", "IngredientName");
}
View
#model RecipeVM
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name)
#Html.ValidationMessageFor(m => m.Name)
for (int i = 0; i < Model.SelectedIngredients.Count; i++)
{
#Html.LabelFor(m => m.SelectedIngredients[i])
#Html.DropDownListFor(m => m.SelectedIngredients[i], Model.IngredientList, "-Please select-")
}
}
Note this is based on your current implementation of creating 5 dropdowns to select 5 ingredients. In reality you will want to dynamically add ingredients (start with none). The answers here and here give you a few options to consider.