I have a form that use DropDownListFor that are set in mvc model. But strangely enough, the current value of them sometimes can not be retrieved in model (sometimes they can). The code is,
<% using (Html.BeginForm("EditPageSubmit", "Home", FormMethod.Post))
{
<%: Html.DropDownListFor(model => model.Destination, Model.Destinations, Model.Destination)%>
...
model.Destinations= (from r in this._repository.Destinations
select new SelectListItem
{
Text = r.Name,
Value = SqlFunctions.StringConvert((double)r.DestinationID),
Selected = false
});
Model.Destination = "...";
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditPageSubmit(FormCollection collection)
{
var updatedModel = new SalesViewModel();
if (TryUpdateModel(updatedModel))
...
}
Strangely enough, sometimes (often when dropdownlist is not changed and remains with initial value), updatedModel.Destination is null, and sometimes it is the selected value. I have really no idea on this problem and need help on it. Thanks.
I find the reason. The initialization variable can not be the same as model id. In this case, simply change to Html.DropDownListFor(model => model.Destination, Model.Destinations, Model.Destination1) will work.
Related
I was hoping for some guidance on an issue I am having with preserving the value in a dropdownlist after post (razor)
I have a simple page:
#model testContingency.Models.ListByWardDD
#{
ViewBag.Title = "TestDropDowns";
}
<h2>TestDropDowns</h2>
<div>
#Html.DropDownList("HospModel", Model.Hospital, new { #onchange = "ChangeHospital(this.value)" })
#Html.DropDownList("WardModel", Model.Wards)
<script type="text/javascript">
function ChangeHospital(val) {
window.location.href = "/PatientListByWardDD/TestDropDowns?hospID=" + val;
}
</script>
</div>
here's the controller
public ActionResult TestDropDowns(int? hospID)
{
PASInpatientRepository pasRepo = new PASInpatientRepository();
var returnModel = new ListByWardDD();
var HospitalData = pasRepo.GetPatientHospitalsEnum();
returnModel.Hospital = pasRepo.GetHopspitalListItems(HospitalData);
var WardData = pasRepo .GetPatientWardsEnum(hospID);
returnModel.Wards = pasRepo.GetWardListItems(WardData);
ViewBag.HospSearch = hospID;
return View(returnModel);
}
In the controller PASInpatientRepository() communicates with a cache database. It passes back public IEnumerable < SelectListItem > GetHopspitalListItems. It calls stored procedures written within a cache database (same as sql stored procedures in essence). This is all working fine in its own crude way.
The issue I am having is that when I select the dropdownlist #Html.DropDownList("HospModel", Model.Hospital, new { #onchange = "ChangeHospital(this.value)" }) and the controller is called to refresh the Wards dropdown, I want to preserve the value I have selected in the hospital dropdown. I have tried a few different ways, but I admit, I'm a bit stuck. Most examples I found are for strongly typed.
As I mentioned, I'm new to MVC, but any advice on how to solve this issue, or suggestions on improving my code are greatly appreciated.
So I'm not sure what the Hospital property looks like but I'll make the assumption that each one has a unique ID.
Furthermore to bind the posted data to the view model you'll need to use forms in your view. To create the drop down list use the DropDownListFor-Helper. This way the data will be bound back to your Model after submitting the form.
So your view could look something like this
#model testContingency.Models.ListByWardDD
#{
ViewBag.Title = "TestDropDowns";
}
<h2>TestDropDowns</h2>
<div>
#using (Html.BeginForm("TestDropDowns", "YourController", FormMethod.Post))
{
#Html.DropDownListFor(x => x.HospitalID, Model.Hospital)
#Html.DropDownListFor(x => x.WardID, Model.Wards)
<input type="submit" value="send" />
}
</div>
Your ViewModel testContigency.Models.ListByWardDD must have at least the following properties
public class ListByWardDD {
public int HostpitalID { get;set; }
// the value of the SelectListItem-objects should be the hospital ID
public IEnumerable<SelectListItem> Hospital { get;set; }
public int WardID { get;set; }
// the value of the SelectListItem-objects should be the ward ID
public IEnumerable<SelectListItem> Wards { get;set; }
}
Once you post the form (for simplicity I added a button to send the form and left the javascript part out) the method TestDropDowns of your controller (which you need to fill in the BeginForm-Helper) will be called. That method expects expects an object of type ListByWardDD as a parameter and the framework will automatically populate the values for you.
[HttpPost]
public ActionResult TestDropDowns(ListByWardDD viewModel) {
// your code here, viewModel.HospitalID should contain the selected value
}
Note: After submitting the form the properties Hospital and Wards will be empty. If you need to display the form again, you need to repopulate those properties. Otherwise your dropdown lists are empty.
I tried my best to post valid code but I did not compile or test it.
Consider the following question and potential answer: ASP.Net MVC Multiple Drop Downs, Single List, Only Allow Unique Selections
Ignoring most of the details, we can see that we can implement many dropdowns for a 1-m relationship like this:
<%: Html.DropDownListFor(model => model.DropwdownId1,Model.DropdownEntities) %>
<%: Html.DropDownListFor(model => model.DropwdownId2,Model.DropdownEntities) %>
<%: Html.DropDownListFor(model => model.DropwdownId3,Model.DropdownEntities) %>
DropdownId1, DropdownId2 and DropdownId3 are properties easily added to the model if and only if we know exactly how many dropdowns we are going to display and postback.
However, I would like to implement a variable number of dropdowns. New dropdowns could, for example, be added dynamically by Javascript. Or the number of dropdowns displayed into the view could be dependent on some variable Property in model. say, model.NumberOfDropDowns.
How can I implement this? How do I write a viewmodel, and controller action that can handle a variable number of dropdowns?
I have done a lot of reading on complex things like editor templates and blog posts where form elements are added dynamically, but I'm really having difficulty trying to figure out how this could be done. Any assistance would be greatly appreciated
A drop-down input still has only 1 submitted form value, so it's the same as any other variable-length view-model property: use a List<T>, like so:
ViewModel
class FooViewModel {
public List<String> DropDownFields { get; set; }
}
Controller Actions
[HttpGet]
ActionResult Index() {
ViewData["dropDownSource"] = new List<SelectListItem>
{
new SelectListItem
{
Text = "Test",
Value= "1"
},
new SelectListItem
{
Text = "Text",
Value= "2"
}
};
return View( new FooViewModel() );
}
[HttpPost]
ActionResult Index(FooViewModel vm) {
for(int i = 0; i < vm.DropDownFields.Count; i++) {
// for each dropdown
}
}
View (aspx syntax)
<% for(int i = 0; i < vm.DropDownFields.Count; i++) { %>
<%= Html.DropDownListFor( m => m.DropDownFields[i], (IEnumerable<SelectListItem>)ViewData["dropDownSource"] ) %>
<% } %>
I need to have multiple radio button groups in my form like this:
I know it's simply done by specifying the same "name" html attribute for each group.
HOWEVER
MVC doesn't let you specify your own name attribute when using html helper like this:
#Html.RadioButtonFor(i => item.id, item.SelectedID, new { Name = item.OptServiceCatId })
Because it looks at each tag's "name" attribute (not "id") to map/bind the form to the model which the controller receives, etc.
Some said that specifying each with the same "GroupName" attribute will solve the problem, but it didn't work either.
So, is there any way which works ?
EDIT:
Here's my view (simplified):
#model Service_Provider.ViewModels.SelectOptServicesForSubServiceViewModel
#foreach (var cat in Model.OptServices)
{
//A piece of code & html here
#foreach (var item in cat.OptItems.Where(i => i.MultiSelect == false))
{
#Html.RadioButtonFor(i => item.id, item.SelectedID, new { GroupName = item.OptServiceCatId })
<br />
}
}
NOTE:
My model is a List<OptServices>:
public List<OptServices> Cats {get; set;}
And OptServices has a List of OptItems inside:
public class OptServices
{
//a few things
public List<OptItems> Items {get; set;}
}
all you need is to tie the group to a different item in your model
#Html.RadioButtonFor(x => x.Field1, "Milk")
#Html.RadioButtonFor(x => x.Field1, "Butter")
#Html.RadioButtonFor(x => x.Field2, "Water")
#Html.RadioButtonFor(x => x.Field2, "Beer")
Ok here's how I fixed this
My model is a list of categories. Each category contains a list of its subcategories.
with this in mind, every time in the foreach loop, each RadioButton will have its category's ID (which is unique) as its name attribue.
And I also used Html.RadioButton instead of Html.RadioButtonFor.
Here's the final 'working' pseudo-code:
#foreach (var cat in Model.Categories)
{
//A piece of code & html here
#foreach (var item in cat.SubCategories)
{
#Html.RadioButton(item.CategoryID.ToString(), item.ID)
}
}
The result is:
<input name="127" type="radio" value="110">
Please note that I HAVE NOT put all these radio button groups inside a form. And I don't know if this solution will still work properly in a form.
Thanks to all of the people who helped me solve this ;)
I fixed a similar issue building a RadioButtonFor with pairs of text/value from a SelectList. I used a ViewBag to send the SelectList to the View, but you can use data from model too. My web application is a Blog and I have to build a RadioButton with some types of articles when he is writing a new post.
The code below was simplyfied.
List<SelectListItem> items = new List<SelectListItem>();
Dictionary<string, string> dictionary = new Dictionary<string, string>();
dictionary.Add("Texto", "1");
dictionary.Add("Foto", "2");
dictionary.Add("Vídeo", "3");
foreach (KeyValuePair<string, string> pair in objBLL.GetTiposPost())
{
items.Add(new SelectListItem() { Text = pair.Key, Value = pair.Value, Selected = false });
}
ViewBag.TiposPost = new SelectList(items, "Value", "Text");
In the View, I used a foreach to build a radiobutton.
<div class="form-group">
<div class="col-sm-10">
#foreach (var item in (SelectList)ViewBag.TiposPost)
{
#Html.RadioButtonFor(model => model.IDTipoPost, item.Value, false)
<label class="control-label">#item.Text</label>
}
</div>
</div>
Notice that I used RadioButtonFor in order to catch the option value selected by user, in the Controler, after submit the form. I also had to put the item.Text outside the RadioButtonFor in order to show the text options.
Hope it's useful!
I was able to use the name attribute that you described in your example for the loop I am working on and it worked, perhaps because I created unique ids? I'm still considering whether I should switch to an editor template instead as mentioned in the links in another answer.
#Html.RadioButtonFor(modelItem => item.Answers.AnswerYesNo, "true", new {Name = item.Description.QuestionId, id = string.Format("CBY{0}", item.Description.QuestionId), onclick = "setDescriptionVisibility(this)" }) Yes
#Html.RadioButtonFor(modelItem => item.Answers.AnswerYesNo, "false", new { Name = item.Description.QuestionId, id = string.Format("CBN{0}", item.Description.QuestionId), onclick = "setDescriptionVisibility(this)" } ) No
You can use Dictonary to map
Assume Milk,Butter,Chesse are group A (ListA)
Water,Beer,Wine are group B
Dictonary<string,List<string>>) dataMap;
dataMap.add("A",ListA);
dataMap.add("B",ListB);
At View , you can foreach Keys in dataMap and process your action
My KendoUI MultiSelect returns only the first selected value, if nothing was changed.
When I load the Viewpage, the MultiSelect is populated correctly with data and multiple selection. When I now proceed to save the data without having changed anything(!), the model transferred to my controller (most of the time, no pattern discernible) only contains the first selected item in the appropiate field. If I add new selections, only the first of the previously selected items is returned together with the freshly selected ones.
If I use .AutoBind(false) and then click once into the field and then outside again without having de-/selected anything, the model transferred to my controller contains the full contents on saving.
With multiple MultiSelects (on different data) on the same ViewPage, it is enough to use .AutoBind(false) on one MultiSelect (with the subsequent click like above), so that all MultiSelects suddenly return the full range of selected values.
Can anyone please explain this strange behaviour to me, possibly even with a fix?
Two different implementations in the View, which both show the same behaviour (GetDepotList() returns similar content to ViewData["DepotList"]):
#model List<LoadingUnitViewModel>
[...]
//Version 1
#(Html.Kendo().MultiSelectFor(m => m[i].FK_Depots)
.Name("[" + i.ToString() + "]." + "FK_Depots")
.Placeholder(Localize((string)ViewData["ControllerName"], "ChooseDepot"))
.DataValueField("Value")
.Value(Model[i].FK_Depots)
.DataTextField("Text")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetDepotList", (string)ViewData["ControllerName"]);
})
.ServerFiltering(true);
})
.HtmlAttributes(new { id = "_" + i + "__FK_Depots"})
)
//Version 2
#(Html.Kendo().MultiSelectFor(m => m[i].FK_Depots)
.Name("["+i.ToString()+"]."+"FK_Depots")
.Placeholder(Localize((string)ViewData["ControllerName"], "ChooseDepot"))
.BindTo(new MultiSelectList((System.Collections.IEnumerable)ViewData["DepotList"], "Id", "Name", Model[i].FK_Depots))
)
Edit:
As requested: The relevant parts of the LoadingUnitViewModel:
public class LoadingUnitViewModel
{
[Editable(false)]
[Display(Name = "Id")]
public int Id { get; set; }
[Required]
[Display(Name = "Name")]
public string Name { get; set; }
[Display(Name = "FK_Depots")]
public List<string> FK_Depots { get; set; }
}
We've run into some weird issues with Kendo MultiSelect and not binding to the MVC Model, I'll show you what we've done. Part of the problem looks like is the name your giving your multiselect. I dnt know why you'd need to generate multiple names like you are doing. If you give it a single name attribute you can get all the values. This is what my multiselect looks like. Notice the Name is simply "Advertisers"
#(Html.Kendo().MultiSelect()
.Name("Advertisers")
.BindTo(Model.Advertisers)
.HtmlAttributes(new { style="width:445px;" })
.DataTextField("Text")
.Filter("startswith")
.MinLength(5)
.MaxSelectedItems(3)
.DataValueField("Value")
.AutoBind(false)
.Placeholder("Type the first five characters of the advertiser name...")
.Value(Model.Advertisers)
.HtmlAttributes(new {style = "width:415px;"})
.Events(e => e.Select("advertiserSelected"))
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetAccounts", "CrmIntegration");
})
.ServerFiltering(true);
})
)
On form submit inside your controller, the values weren't going to the model, so instead we pulled them like this.
string[] advertisers = Request.Form["Advertisers"].ToString().Split(',');
foreach (var s in advertisers)
{
// access to the values in multiselect
}
Edit
It should be noted that default model binder will look to bind the value to the property named in .Name("Property") regardless whether you use MultiSelectFor or not. I realized this after posting this answer.
#(Html.Kendo().MultiSelectFor(x => x.AdvId)
.Name("Advertisers")
//will bind to Advertisers, not AdvId on post
I'm experiencing following issue: if I submit a form that contains checked value in checkbox to the api controller via AJAX, ModelState object says it's invalid.
Prerequisites:
Visual Studio 2012
ASP.NET MVC 4 Final
Latest jQuery and jQuery unobtrusive validation stuff (versions 1.8.2 and 1.9.0.1)
Steps to reproduce:
Created a ViewModel for form, containing Boolean field:
public class CheckboxViewModel
{
[Display(Name="Test checkbox")]
public bool TestCheckbox { get; set; }
}
Created controller action that simply prepares ViewModel and renders form:
public ActionResult Index()
{
return View(new CheckboxViewModel());
}
Created a view with form
#using (Ajax.BeginForm("Post", "api/Values", null, new AjaxOptions { HttpMethod = "POST" }, new { id = "form" }))
{
#Html.ValidationSummary(true)
#Html.LabelFor(model => model.TestCheckbox)
#Html.EditorFor(model => model.TestCheckbox)
#Html.ValidationMessageFor(model => model.TestCheckbox)
<input type="submit" value="Submit" />
}
Created Web API action that checks if modelstate is valid and return status code 200,otherwise 400:
public HttpResponseMessage Post(CheckboxViewModel model)
{
if (!ModelState.IsValid)
return new HttpResponseMessage(HttpStatusCode.BadRequest);
return new HttpResponseMessage(HttpStatusCode.OK);
}
I've done some googling and I know that editor for checkbox renders additional hidden field, but it seems to be ok and is needed for model binding.
So, when I uncheck checkbox and submit form everything works fine, server responds with status 200, thanks to hidden field, I suppose. However, when I make checkbox checked and submit form, server responds with status 400, meaning that ModelState is in invalid state. ModelState in this case contains error with empty ErrorMessage and following Exception message:
{System.InvalidOperationException: The parameter conversion from type 'System.Collections.Generic.List`1[System.String]' to type 'System.Boolean' failed because no type converter can convert between these types.
в System.Web.Http.ValueProviders.ValueProviderResult.ConvertSimpleType(CultureInfo culture, Object value, Type destinationType)
в System.Web.Http.ValueProviders.ValueProviderResult.UnwrapPossibleArrayType(CultureInfo culture, Object value, Type destinationType)
в System.Web.Http.ValueProviders.ValueProviderResult.ConvertTo(Type type, CultureInfo culture)
в System.Web.Http.ValueProviders.ValueProviderResult.ConvertTo(Type type)
в System.Web.Http.ModelBinding.Binders.TypeConverterModelBinder.BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)}
I have no idea how to handle this properly. Should I create custom model binder? Or am I missing something?
Any help would be appreciated. Thanks.
I've created solution that reproduces my issue.
UPDATE: I've figured out that form actually contains values TestCheckbox: true and TestCheckbox:false, so I think this maybe some kind affects binder and it throws an exception. Still have no options how to workaround this.
I ran into the same problem sending a form to a Web API with JavaScript. Here is a basic fix you can use to work around the problem. I know it's kind of simplistic or even ugly - and not very practical if you have lots of checkboxes. However, the idea is simple and could easily be extended to meet your requirements.
Add this before you post the form (using jQuery):
if ($('[name="MyCheckbox"]:checked').length > 0)
$('[name="MyCheckbox"]:hidden').detach();
else if ($('[name="MyCheckbox"]:hidden').length < 1)
$('#myForm').append('<input type="hidden" name="MyCheckbox" value="false" />');
// Etc...
$.ajax({ });
So you remove the hidden field if the checkbox is checked, and you add it if the checkbox isn't checked and the hidden field has already been deleted.
See this Fiddle: http://jsfiddle.net/Hsfdg/
I tried to implement this
if ($('[name="MyCheckbox"]:checked').length > 0)
$('[name="MyCheckbox"]:hidden').detach();
else if ($('[name="MyCheckbox"]:hidden').length < 1)
$('#myForm').append('<input type="hidden" name="MyCheckbox" value="false" />');
But ended up the model property being removed when hidden check box is detached.
Instad set the hidden chechbox value to true results in setting the model property to true the value expected else false by default.
if ($('[name="MyCheckbox"]:checked').length > 0)
$('[name="MyCheckbox"]:hidden').val(true);