To populate a dropdown in mvc3 - asp.net-mvc-3

I am new to MVC3
I am finding it difficult to create an dropdown.I have gone through all the other related questions but they all seem to be complex
I jus need to create a dropdown and insert the selected value in database
Here is what i have tried:
//Model class:
public int Id { get; set; }
public SelectList hobbiename { get; set; }
public string filelocation { get; set; }
public string hobbydetail { get; set; }
//Inside Controller
public ActionResult Create()
{
var values = new[]
{
new { Value = "1", Text = "Dancing" },
new { Value = "2", Text = "Painting" },
new { Value = "3", Text = "Singing" },
};
var model = new Hobbies
{
hobbiename = new SelectList(values, "Value", "Text")
};
return View();
}
//Inside view
<div class="editor-label">
#Html.LabelFor(model => model.hobbiename)
</div>
<div class="editor-field">
#Html.DropDownListFor( x => x.hobbiename, Model.hobbiename )
#Html.ValidationMessageFor(model => model.hobbiename)
</div>
I get an error:System.MissingMethodException: No parameterless constructor defined for this object

You are not passing any model to the view in your action. Also you should not use the same property as first and second argument of the DropDownListFor helper. The first argument that you pass as lambda expression corresponds to a scalar property on your view model that will hold the selected value and which will allow you to retrieve this value back when the form is submitted. The second argument is the collection.
So you could adapt a little bit your code:
Model:
public class Hobbies
{
[Required]
public string SelectedHobbyId { get; set; }
public IEnumerable<SelectListItem> AvailableHobbies { get; set; }
... some other properties that are irrelevant to the question
}
Controller:
public class HomeController: Controller
{
public ActionResult Create()
{
// obviously those values might come from a database or something
var values = new[]
{
new { Value = "1", Text = "Dancing" },
new { Value = "2", Text = "Painting" },
new { Value = "3", Text = "Singing" },
};
var model = new Hobbies
{
AvailableHobbies = values.Select(x => new SelectListItem
{
Value = x.Value,
Text = x.Text
});
};
return View(model);
}
[HttpPost]
public ActionResult Create(Hobbies hobbies)
{
// hobbies.SelectedHobbyId will contain the id of the element
// that was selected in the dropdown
...
}
}
View:
#model Hobbies
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.SelectedHobbyId)
#Html.DropDownListFor(x => x.SelectedHobbyId, Model.AvailableHobbies)
#Html.ValidationMessageFor(x => x.SelectedHobbyId)
<button type="submit">Create</button>
}

I would create them as
Model:
public class ViewModel
{
public int Id { get; set; }
public string HobbyName { get; set; }
public IEnumerable<SelectListItem> Hobbies {get;set; }
public string FileLocation { get; set; }
public string HobbyDetail { get; set; }
}
Action
public ActionResult Create()
{
var someDbObjects= new[]
{
new { Id = "1", Text = "Dancing" },
new { Id = "2", Text = "Painting" },
new { Id = "3", Text = "Singing" },
};
var model = new ViewModel
{
Hobbies = someDbObjects.Select(k => new SelectListItem{ Text = k, Value = k.Id })
};
return View(model);
}
View
<div class="editor-label">
#Html.LabelFor(model => model.HobbyName)
</div>
<div class="editor-field">
#Html.DropDownListFor(x => x.HobbyName, Model.Hobbies )
#Html.ValidationMessageFor(model => model.HobbyName)
</div>

Related

MVC 3 Model binding and No parameterless constructor defined for this object

I created a view that was working wonderfully until I added some JQuery to support cascading drop downs. I believe in doing that, I broke the binding between the view and the model. I'm getting the error "No parameterless constructor defined for this object." when the form is submitted. The obvious solution would be to add a parameterless constructor, but I'm assuming that the postmodel will be null? Code Snippets below.
Thanks in Advance for your help.
View:
<script type="text/javascript">
$(document).ready(function () {
$("#ddlCategories").change(function () {
var iCategoryId = $(this).val();
$.getJSON(
"#Url.Content("~/Remote/SubCategoriesByCateogry")",
{ id: iCategoryId },
function (data) {
var select = ResetAndReturnSubCategoryDDL();
$.each(data, function (index, itemData) {
select.append($('<option/>', { value: itemData.Value, text: itemData.Text }));
});
});
});
function ResetAndReturnSubCategoryDDL() {
var select = $('#ddlSubCategory');
select.empty();
select.append($('<option/>', { value: '', text: "--Select SubCategory--" }));
return select;
}
});
...
<div class="editor-field">
#Html.DropDownList("iCategoryID", Model.Categories,"--Select Category--", new Dictionary<string,object>{ {"class","dropdowns"},{"id","ddlCategories"}})
#Html.ValidationMessage("iCategoryID")
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SubCategories, "SubCategory")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.SubCategories, new SelectList(Enumerable.Empty<SelectListItem>(), "iSubCategoryID", "SubCategory",Model.SubCategories), "--Select SubCategory--", new { id = "ddlSubCategory" })
#Html.ValidationMessage("iSubCategoryID")
</div>
Controller:
[HttpPost]
public ActionResult Create(VendorCreateModel postModel)
{
VendorCreateEditPostValidator createValidator = new VendorCreateEditPostValidator(
postModel.iCategoryID,
postModel.iSubCategoryID,
postModel.AppliedPrograms,
m_unitOfWork.ProgramRepository,
new ModelStateValidationWrapper(ModelState));
if (ModelState.IsValid)
{
int categoryId = int.Parse(postModel.iCategoryID);
int subcategoryId = int.Parse(postModel.iSubCategoryID);
var programIds = postModel.AppliedPrograms.Select(ap => int.Parse(ap));
var programs = m_unitOfWork.ProgramRepository.GetPrograms(programIds);
Vendor vendor = postModel.Vendor;
vendor.Category = m_unitOfWork.CategoryRepository.GetCategory(categoryId);
vendor.SubCategory = m_unitOfWork.SubCategoryRepository.GetSubCategory(subcategoryId);
foreach (Program p in programs)
vendor.Programs.Add(p);
m_unitOfWork.VendorRepository.Add(vendor);
m_unitOfWork.SaveChanges();
return RedirectToAction("Index");
}
VendorCreateModel model = new VendorCreateModel(
postModel.Vendor,
postModel.iCategoryID,
postModel.iSubCategoryID,
postModel.AppliedPrograms,
User.Identity.Name,
m_unitOfWork.CategoryRepository,
m_unitOfWork.SubCategoryRepository,
m_unitOfWork.PermissionRepository);
return View(model);
}
RemoteController:
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult SubCategoriesByCateogry(int id)
{
System.Diagnostics.Debug.WriteLine(id);
var SubCategories = db.SubCategories
.Where(v => v.iCategoryID == id)
.OrderBy(v => v.sDesc)
.ToList();
var modelData = SubCategories.Select(v => new SelectListItem()
{
Text = v.sDesc,
Value = v.iSubCategoryID.ToString()
});
return Json(modelData, JsonRequestBehavior.AllowGet);
}
VendorCreateModel:
public class VendorCreateModel
{
public VendorCreateModel()
{
}
public VendorCreateModel(
Vendor vendor,
string categoryId,
string subcategoryId,
IEnumerable<string> appliedPrograms,
string username,
ICategoryRepository categoryRepository,
ISubCategoryRepository subcategoryRepository,
IPermissionRepository permissionRepository)
{
UserHasProgramsValidator programValidator = new UserHasProgramsValidator(username, permissionRepository);
var availablePrograms = programValidator.AvailablePrograms;
HashSet<Category> applicableCategories = new HashSet<Category>();
foreach (var p in availablePrograms)
foreach (var c in categoryRepository.GetCategoriesByProgram(p.iProgramID))
applicableCategories.Add(c);
this.Vendor = vendor;
this.AppliedPrograms = appliedPrograms;
this.Categories = new SelectList(applicableCategories.OrderBy(x => x.sDesc).ToList(), "iCategoryID", "sDesc");
this.SubCategories = new SelectList(subcategoryRepository.GetAllSubCategories().OrderBy(x => x.sDesc).ToList(), "iSubCategoryID", "sDesc");
if (!string.IsNullOrEmpty(categoryId))
{
int temp;
if (!int.TryParse(categoryId, out temp))
throw new ApplicationException("Invalid Category Identifier.");
}
this.iCategoryID = categoryId;
this.iSubCategoryID = subcategoryId;
this.ProgramItems = availablePrograms
.Select(p => new SelectListItem()
{
Text = p.sDesc,
Value = p.iProgramID.ToString(),
Selected = (AppliedPrograms != null ? AppliedPrograms.Contains(p.iProgramID.ToString()) : false)
});
}
public Vendor Vendor { get; set; }
public SelectList Categories { get; set; }
public SelectList SubCategories { get; set; }
public string iCategoryID { get; set; }
public string iSubCategoryID { get; set; }
public IEnumerable<SelectListItem> ProgramItems { get; set; }
[AtLeastOneElementExists(ErrorMessage = "Please select at least one program.")]
public IEnumerable<string> AppliedPrograms { get; set; }
}
I correct the issue and wanted to share in case someone else was banging their head against their desk like Ihave been. Basically I changed the dropdownlistfor to reflect:
#Html.DropDownListFor(model => model.iSubCategoryID, new SelectList(Enumerable.Empty<SelectListItem>(), "iSubCategoryID", "SubCategory",Model.SubCategories), "--Select SubCategory--", new Dictionary<string,object>{ {"class","dropdowns"},{"id","ddlSubCategory"},{"name","iSubCategoryID"}})
Assuming here the problem is in your VendorCreateModel, you either need to add a parameterless constructor or remove it, and create an instance in your action method and populate it by TryUpdateModel. Or parse the form using FormsCollection (not a fan).
You don't have the code for your viewmodel posted here but the basic assumption is that it will map.

SelectList in MVC3

I have a MVC3 page that has to dropdownList contains Sex(male,female) and Role(admin,Operator), in RegisterViewModel I have done like this :
public List<SelectListItem> SelectedItemForSex { get; set; }
public List<SelectListItem> SelectedItemForRole { get; set; }
public string SearchText { get; set; }
public string SelectedValue { get; set; }
//public string SelectedValueForRole { get; set; }
public static RegisterViewModel Get()
{
var model = new RegisterViewModel { SelectedItemForSex = new List<SelectListItem>() };
model.SelectedItemForSex.Add(new SelectListItem() { Text = "Male", Value = "1" });
model.SelectedItemForSex.Add(new SelectListItem() { Text = "Femle", Value = "2" });
model.SelectedItemForRole.Add(new SelectListItem() {Text = "Administrator", Value = "1"});
model.SelectedItemForRole.Add(new SelectListItem() {Text = "Operator", Value = "2"});
return model;
}
and in get action of Register I have this code :
public ActionResult Create()
{
var model = RegisterViewModel.Get();
return View(model);
}
and also in razor :
<div>
#Html.LabelFor(register => register.Sex)
#Html.DropDownListFor(register => register.SelectedValue, new SelectList(Model.SelectedItemForSex, "Value", "Text"))
</div>
<div>
#Html.LabelFor(register => register.Role)
#Html.DropDownListFor(register => register.SelectedValue, new SelectList(Model.SelectedItemForRole, "Value", "Text"))
</div>
<div>
I know I have Not initialize selectedListItem for Administrator and operator , I wanna to send all of this 4 value via Get() method, How can I do this ??

Data binding MVC3

I have no idea what am i doing wrong.
Well i have this form, it's part of complex view.
#{
var filtersAjaxOptions = new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "clientList-body",
OnBegin = "clientList.filterRequestStart()",
OnComplete = "clientList.filterRequestComplete()",
OnSuccess = "clientList.filterRequestSuccess()"
};
}
<span class="clientFilters-filterValue inlineBlock">
#using (Ajax.BeginForm(
"Index",
"ClientList",
new {
ProductId = Model.ClientListViewModel.Filters.ProductId,
ClientFilter = Model.ClientListViewModel.Filters.ClientFilter,
BillFilter = Model.ClientListViewModel.Filters.BillFilter,
DateSortType = Model.ClientListViewModel.Filters.DateSortType,
SortDirection = Model.ClientListViewModel.Filters.SortDirection
},
filtersAjaxOptions,
new
{
id = "clientListDateFilter-form"
}
))
{
#Html.TextBoxFor(
m => m.ClientListViewModel.Filters.BeginDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterStart",
}
)
#Html.TextBoxFor(
m => m.ClientListViewModel.Filters.EndDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterEnd",
}
)
}
</span>
Here's the filters model
public class FilterModel
{
public FilterModel()
{
ClientFilter = ClientsEnum.All;
BillFilter = ClientBillsEnum.All;
}
public string ProductId { get; set; }
public ClientsEnum ClientFilter { get; set; }
public ClientBillsEnum BillFilter { get; set; }
public DateTime? BeginDateRange { get; set; }
public DateTime? EndDateRange { get; set; }
public DateSortType? DateSortType { get; set; }
public SortDirection? SortDirection { get; set; }
}
This part is ClientListController method Index:
public ActionResult Index(FilterModel filters)
{
var clientListViewModel = GetClientListViewModel(filters, 1, 1, PageSize);
if (ControllerContext.HttpContext.Request.IsAjaxRequest())
return PartialView("Partial/ClientListBody", clientListViewModel);
return View(clientListViewModel);
}
Whenever i submit the form above, it turns to me that fields "BeginDateRange" and "EndDateRange" are null and other fields are set properly. Although, when i insert Request.Form in Watch, i can see the whole data.
UPDATE 1
So i set the <globalisation> in Web.config as this:
<globalisation responseHeaderEncoding="utf-8" culture="en-US">
and yet it doesn't work. Very same result as before.
UPDATE 2
Also when i tried to put all the routevalues data into #Html.HiddenFor, controller saw only nulls. And again, Request.Form is filled prprly.
So the question is: how can i bind form data to incoming model?
TY
The default model binder uses the current culture datetime format when binding datetimes. This means that you have to enter the date into the proper format in your textboxes. On the other hand if you need a fixed format you could use a fixed culture in your web.config (<globalization> element) or write a custom model binder: https://stackoverflow.com/a/7836093/29407
UPDATE:
You need to specify the correct binding prefix because your input fields are named like ClientListViewModel.Filters.BeginDateRange but your controller action takes a FilterModel as parameter instead of the root view model:
public ActionResult Index([Bind(Prefix = "ClientListViewModel.Filters")] FilterModel filters)
{
...
}
But now this will break the other values, so you need to adjust your view as well:
#using (Ajax.BeginForm(
"Index",
"ClientList",
null,
filtersAjaxOptions,
new
{
id = "clientListDateFilter-form"
}
))
{
#Html.HiddenFor(x => x.ClientListViewModel.Filters.ProductId)
#Html.HiddenFor(x => x.ClientListViewModel.Filters.ClientFilter)
#Html.HiddenFor(x => x.ClientListViewModel.Filters.BillFilter)
#Html.HiddenFor(x => x.ClientListViewModel.Filters.DateSortType)
#Html.HiddenFor(x => x.ClientListViewModel.Filters.SortDirection)
#Html.TextBoxFor(
m => m.ClientListViewModel.Filters.BeginDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterStart",
}
)
#Html.TextBoxFor(
m => m.ClientListViewModel.Filters.EndDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterEnd",
}
)
}
or if you want to send them as part of the form url instead if using hidden fields:
#using (Ajax.BeginForm(
"Index",
"ClientList",
new RouteValueDictionary
{
{ "ClientListViewModel.Filters.ProductId", Model.ClientListViewModel.Filters.ProductId },
{ "ClientListViewModel.Filters.ClientFilter", Model.ClientListViewModel.Filters.ClientFilter },
{ "ClientListViewModel.Filters.BillFilter", Model.ClientListViewModel.Filters.BillFilter },
{ "ClientListViewModel.Filters.DateSortType", Model.ClientListViewModel.Filters.DateSortType },
{ "ClientListViewModel.Filters.SortDirection", Model.ClientListViewModel.Filters.SortDirection },
},
filtersAjaxOptions,
new RouteValueDictionary
{
{ "id", "clientListDateFilter-form" }
}
))
{
#Html.TextBoxFor(
m => m.ClientListViewModel.Filters.BeginDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterStart",
}
)
#Html.TextBoxFor(
m => m.ClientListViewModel.Filters.EndDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterEnd",
}
)
}
Try this:
public ActionResult Index(FilterModel filters, FormCollection collection)
{
UpdateModel(filters, "ClientListViewModel");
var clientListViewModel = GetClientListViewModel(filters, 1, 1, PageSize);
if (ControllerContext.HttpContext.Request.IsAjaxRequest())
return PartialView("Partial/ClientListBody", clientListViewModel);
return View(clientListViewModel);
}
And in view:
#Html.TextBoxFor(
m => m.ClientListViewModel.FilterModel.EndDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterEnd",
}
)
You have strange naming. Also it would be better to use hidden fields then passing values through routevalues.

MVC3 dropdown list that displays mutliple properties per item

I want to have a dropdownlist in my view that displays a patient's ID, First Name, and Last Name. With the code below, it displays each patient's First Name. How can I pass all three properties into the viewbag and have them display in the dropdownlist?
Controller
public ActionResult Create()
{ViewBag.Patient_ID = new SelectList(db.Patients, "Patient_ID", "First_Name");
return View();
}
View
<div class="editor-field">
#Html.DropDownList("Patient_ID", String.Empty)
#Html.ValidationMessageFor(model => model.Patient_ID)
</div>
Thanks.
Ok, I have edited my code as follows, and I receive the error "There is no ViewData item of type 'IEnumerable' that has the key 'SelectedPatientId'."
Controller
public ActionResult Create()
{
var model = new MyViewModel();
{
var Patients = db.Patients.ToList().Select(p => new SelectListItem
{
Value = p.Patient_ID.ToString(),
Text = string.Format("{0}-{1}-{2}", p.Patient_ID, p.First_Name, p.Last_Name)
});
var Prescribers = db.Prescribers.ToList().Select(p => new SelectListItem
{
Value = p.DEA_Number.ToString(),
Text = string.Format("{0}-{1}-{2}", p.DEA_Number, p.First_Name, p.Last_Name)
});
var Drugs = db.Drugs.ToList().Select(p => new SelectListItem
{
Value = p.NDC.ToString(),
Text = string.Format("{0}-{1}-{2}", p.NDC, p.Name, p.Price)
});
};
return View(model);
}
View Model
public class MyViewModel
{
[Required]
public int? SelectedPatientId { get; set; }
public IEnumerable<SelectListItem> Patients { get; set; }
[Required]
public int? SelectedPrescriber { get; set; }
public IEnumerable<SelectListItem> Prescribers { get; set; }
[Required]
public int? SelectedDrug { get; set; }
public IEnumerable<SelectListItem> Drugs { get; set; }
}
View
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
#Html.DropDownListFor(
x => x.SelectedPatientId,
Model.Patients,
"-- Select patient ---"
)
#Html.ValidationMessageFor(x => x.SelectedPatientId)
<button type="submit">OK</button>
#Html.DropDownListFor(
x => x.SelectedPrescriber,
Model.Patients,
"-- Select prescriber ---"
)
#Html.ValidationMessageFor(x => x.SelectedPrescriber)
<button type="submit">OK</button>
}
I would recommend you not to use any ViewBag at all and define a view model:
public class MyViewModel
{
[Required]
public int? SelectedPatientId { get; set; }
public IEnumerable<SelectListItem> Patients { get; set; }
}
and then have your controller action fill and pass this view model to the view:
public ActionResult Create()
{
var model = new MyViewModel
{
Patients = db.Patients.ToList().Select(p => new SelectListItem
{
Value = p.Patient_ID.ToString(),
Text = string.Format("{0}-{1}-{2}", p.Patient_ID, p.First_Name, p.Last_Name)
});
};
return View(model);
}
and finally in your strongly typed view display the dropdown list:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(
x => x.SelectedPatientId,
Model.Patients,
"-- Select patient ---"
)
#Html.ValidationMessageFor(x => x.SelectedPatientId)
<button type="submit">OK</button>
}
Easy way to accomplish that is just creating additional property on your model either by modifying model class or adding/modifying a partial class
[NotMapped]
public string DisplayFormat
{
get
{
return string.Format("{0}-{1}-{2}", Patient_ID, First_Name, Last_Name);
}
}

drop down list validation message mvc

In my viewModel I have
public string Addressline1 { get; set; }
public List<SelectListItem> StateList
{
get
{
return State.GetAllStates().Select(state => new SelectListItem { Selected = false, Text = state.Value, Value = state.Value }).ToList();
}
}
In the view I have
#Html.DropDownListFor(model => model.StateCode, Model.StateList, "select")
when AddressLine1 is entered, then the statelist DropDownList selection is Required.How do I validate and show an error message when no state is selected in the Drop down list other than default "select" value?
Decorate your StateCode property with the [Required] attribute:
[Required(ErrorMessage = "Please select a state")]
public string StateCode { get; set; }
public IEnumerable<SelectListItem> StateList
{
get
{
return State
.GetAllStates()
.Select(state => new SelectListItem
{
Text = state.Value,
Value = state.Value
})
.ToList();
}
}
and then you could add a corresponding validation error message:
#Html.DropDownListFor(model => model.StateCode, Model.StateList, "select")
#Html.ValidationMessageFor(model => model.StateCode)
UPDATE:
Alright it seems that you want to conditionally validate this StateCode property depending on some other property on your view model. Now that's an entirely different story and you should have explained this in your original question. Anyway, one possibility is to write a custom validation attribute:
public class RequiredIfPropertyNotEmptyAttribute : ValidationAttribute
{
public string OtherProperty { get; private set; }
public RequiredIfPropertyNotEmptyAttribute(string otherProperty)
{
if (otherProperty == null)
{
throw new ArgumentNullException("otherProperty");
}
OtherProperty = otherProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(OtherProperty);
if (property == null)
{
return new ValidationResult(string.Format(CultureInfo.CurrentCulture, "{0} is an unknown property", new object[]
{
OtherProperty
}));
}
var otherPropertyValue = property.GetValue(validationContext.ObjectInstance, null) as string;
if (string.IsNullOrEmpty(otherPropertyValue))
{
return null;
}
if (string.IsNullOrEmpty(value as string))
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
and now decorate your StateCode property with this attribute, like so:
public string AddressLine1 { get; set; }
[RequiredIfPropertyNotEmpty("AddressLine1", ErrorMessage = "Please select a state")]
public string StateCode { get; set; }
Now assuming you have the following form:
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.AddressLine1)
#Html.EditorFor(x => x.AddressLine1)
</div>
<div>
#Html.LabelFor(x => x.StateCode)
#Html.DropDownListFor(x => x.StateCode, Model.States, "-- state --")
#Html.ValidationMessageFor(x => x.StateCode)
</div>
<input type="submit" value="OK" />
}
the StateCode dropdown will be required only if the user entered a value into the AddressLine1 field.

Resources