ASP.NET MVC cannot POST view model with binding to checkbox - c#

Here are my view models:
public class UserViewModel
{
public User GroupUser { get; set; }
public bool Checked { get; set; }
}
public class GroupUserViewModel
{
public Guid GroupId { get; set; }
public string GroupName { get; set; }
public IList<UserViewModel> Users;
}
My view:
#model GroupUserViewModel
#{
ViewBag.Title = "Users";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>#Model.GroupName</h2>
#using (Html.BeginForm("AddUserToGroup", "Group", FormMethod.Post))
{
for (var userIter = 0; userIter < Model.Users.Count(); userIter++)
{
<div class="form-group">
#Html.DisplayFor(model => model.Users[userIter].GroupUser.UserName)
#Html.CheckBoxFor(model => model.Users[userIter].Checked)
#Html.HiddenFor(model => model.GroupId)
#Html.HiddenFor(model => model.GroupName)
</div>
}
<input type="submit" class="btn btn-success" value="Save"/>
}
My controller:
[HttpPost]
public ActionResult AddUserToGroup(GroupUserViewModel groupUsers)
{
//do things
}
I had an inspection of the POST data and it is:
Users[0].Checked=true
Users[0].Checked=false
Users[1].Checked=false
For the boxes I've ticked there are 2 entries, one true and one false. Is this normal?
Also in the controller, what I get back is:
groupUsers.GroupId = {00000000-0000-0000-0000-000000000000}
groupUsers.GroupName = null
groupUsers.Users = null
Obviously since the form isn't actually posting the view model I want back to the controller, this happens. Is there any way to pass the required view model back to the controller since I need the GroupId and GroupName?
EDIT:
After some updates and adding in hidden fields for GroupId and GroupName, now the POST data is:
Users[0].Checked=true
Users[0].Checked=false
Id=015f5aef-eb6c-449e-9f08-9d42110c5347
GroupName=MyName
MyObjects[1].Checked=false
Id=015f5aef-eb6c-449e-9f08-9d42110c5347
GroupName=MyName
The GroupId and GroupName are now being passed corrently but the list is still null.

Yes this is normal. The reason is because an unchecked checkbox will not post a value, so ASP.NET renders a hidden field for every checkbox with the same ID as the checkbox just after the checkbox control, and sets its value to false. ASP.NET DefaultModelBinder will, if there are multiple form fields with the same name, take the first value. This results in one value of false being posted from the hidden field if the checkbox is not checked, and two values, one of false for the hidden field and one of true for the checked checkbox. Because the hidden field comes after the checkbox, if the checkbox posts a value, it will override the hidden field.
However, that doesn't answer your question as to why the model isn't binding..

IList<UserViewModel> Users in GroupUserViewModel is a field, not a property so the DefaultModelBinder cannot set its value. Change it to
public class GroupUserViewModel
{
....
public IList<UserViewModel> Users { get; set; } // make it a property
}

Related

Get Query Parameter from URL into MVC View

I have an a href link to a page which adds a parameter to the link for example:
tsw/register-your-interest?Course=979
What I am trying to do is to extract the value in Course i.e 979 and display it in the view. When attempting with the below code, I only return with 0 rather than the course value expected. ideally I'd like to avoid using routes.
Here is the view:
<div class="contact" data-component="components/checkout">
#using (Html.BeginUmbracoForm<CourseEnquiryPageSurfaceController>("PostCourseEnquiryForm", FormMethod.Post, new { id = "checkout__form" }))
{
//#Html.ValidationSummary(false)
#Model.Course;
}
And my controller:
public ActionResult CourseEnquiry(string Course)
{
var model = Mapper.Map<CourseEnquiryVM>(CurrentContent);
model.Course = Request.QueryString["Course"];
return model
}
This is the View Model:
public class CourseEnquiryVM : PageContentVM
{
public List<OfficeLocation> OfficeLocations { get; set; }
public string Test { get; set; }
public string Course { get; set; }
public List<Source> SourceTypes { get; set; }
}
SOLUTION:
After some research and comments I've adjusted the code to the below which now retrieves the value as expected
#Html.HiddenFor(m => m.Course, new { Value = #HttpContext.Current.Request.QueryString["Course"]});
Thanks all
Based on the form code you provided you need to use #Html.HiddenFor(m => m.Course) instead of just #Model.Course. #Model.Course just displays the value as text instead of building a input element that will be sent back to your controller.
If your problem is with a link prior to the view you referenced above, here's what I'd expect to work:
View with link:
#model CourseEnquiryVM
#Html.ActionLink("MyLink","CourseEnquiry","CourseController", new {course = #Model.Course}, null)
CourseController:
public ActionResult CourseEnquiry(string course)
{
// course should have a value at this point
}
In your view, you are only displaying the value of Course.. which isn't able to be submitted. You need to incorporate the value of course with a form input element (textbox, checkbox, textarea, hidden, etc.).
I would highly suggest using EditorFor or Textboxfor, but because your controller action is expecting just a string parameter you could just use Editor or TextBox.
#using (Html.BeginUmbracoForm<CourseEnquiryPageSurfaceController>("PostCourseEnquiryForm", FormMethod.Post, new { id = "checkout__form" }))
{
//#Html.ValidationSummary(false)
#Html.TextBox(Model.Course, null, new { #class = "form-control"});
<input type="submit" value="Submit" />
}
Then you should just be able to do this in your controller:
public ActionResult CourseEnquiry(string course) // parameter variables are camel-case
{
var model = Mapper.Map<CourseEnquiryVM>(CurrentContent);
if(!string.IsNullOrWhiteSpace(course))
model.Course = course;
return model;
}
Let me know if this helps.

How to pass back model to controller using razor dropdownlist

I have a DropDownListFor control that I am wanting to show a display value that resides in a property within a model/class (this is the Rule class.) The view's model is actually a collection of these model/classes. However, when I select the item from the DropDownList, I want to send back the entire model as a parameter. I have this working perfectly with the following code, but the Name property within the parameter is coming back as null. The other properties all have appropriate values.
View Code:
#model List<StockTrader.Web.Data.Rule>
#{
ViewBag.Title = "Configure Rules";
}
<h2>#ViewBag.Title</h2>
<h4>Choose a rule to edit:</h4>
<form method="post" id="rulesform" action="SaveRules">
#Html.DropDownListFor(m => m.First().RuleID, new SelectList(Model.AsEnumerable(), "RuleID", "Name"))
<div style="margin-bottom: 15px;">
<label>Value:</label><br />
<input type="number" name="Value" style="margin-bottom: 15px;" /><br />
<button>Save Value</button>
</div>
Controller Code:
public ActionResult SaveRules(Rule model)
{
//do something
}
Rule Class:
public class Rule
{
public int RuleID { get; set; }
public string Name { get; set; }
public int Value { get; set; }
public bool IsDeleted { get; set; }
}
We do have Kendo controls, so if another control would be more appropriate, that is an option.
I would be glad to provide anymore code or information you might need.
Any thoughts or ideas?
EDIT:
So it turns out this is what I needed to do, the accepted answer got me to this point so I'm going to leave it checked.
View Code (w/script included):
#Html.DropDownListFor(m => m.First().RuleID, new SelectList(Model.AsEnumerable(), "RuleID", "Name"), new { id = "ruleid", #onchange = "CallChangefunc(this)" })
#Html.HiddenFor(m => m.First().Name, new { id = "rulename" })
function CallChangefunc(e) {
var name = e.options[e.selectedIndex].text;
$("#rulename").val(name);
}
You will need a hidden field for it,and use dropdownlist on change event on client side to update hidden field:
#Html.DropDownListFor(m => m.First().RuleID, new SelectList(Model.AsEnumerable(), "RuleID", "Name"),new { id= "ruleid" })
#Html.HiddenFor(m=>m.First().Name,new { id="rulename" })
and jquery code:
$("#ruleid").change(function(){
$("#rulename").val($(this).text());
});
Second option isif Rule collection is coming from database you can fetch RuleName by using id to by querying db in action.
it can be achieved by using UIHint
On your model class, on the RuleID property, add an annotation for UIHint. It basically lets you render a partial (cshtml) for the property. So, on the partial, you can have the template for generating the dropdwon with required styling. When Page is generated. Now you can use the same Html.DropDownListFor for RuleID and UI generates a dropdown for it.
This will avoid having additional jQuery code to get the dropdown value, and code is more concise and testable.

MVC models don't persist in a form

I declare a model with 4 string fields. 3 of which are read-only on the form:
public class HomeModel
{
[ReadOnly(true)]
[DisplayName("Service Version")]
public string ServiceVersion { get; set; }
[ReadOnly(true)]
[DisplayName("Session Id")]
public string SessionId { get; set; }
[ReadOnly(true)]
[DisplayName("Visiting from")]
public string Country { get; set; }
[DisplayName("Search")]
public string SearchString { get; set; }
}
I pass the model, after populating it, to my form:
[HttpGet]
public ActionResult Index()
{
var model = new HomeModel
{
Country = "Australia",
SearchString = "Enter a search",
ServiceVersion = "0.1",
SessionId = "76237623763726"
};
return View(model);
}
And the form is displayed as I expect:
<h2>Simple Lookup</h2>
#Html.LabelFor(m=>m.ServiceVersion): #Model.ServiceVersion<br/>
#Html.LabelFor(m=>m.SessionId): #Model.SessionId<br/>
#Html.LabelFor(m=>m.Country): #Model.Country<br/>
<p>
#using(Html.BeginForm())
{
#Html.LabelFor(m => m.SearchString)
#Html.TextBoxFor(m => m.SearchString)
<button type="submit" name="btnSearch">Search</button>
}
</p>
But, when I submit the form, and get the model back from the form, only the value of the SearchString is populated.
[HttpPost]
public ActionResult Index(HomeModel model)
{
return View(model);
}
Is it right that the other fields have been 'lost'? Does MVC not preserve the other members of the model class? And if this is expected - is there a way to re-get these? Or would I need to go back to my database, populate the model with the old values, and then use the new values from the form model?
It's possible the validity of wanting to read 'read-only' fields back from the model is questioned.. which is fair - but in the event that I find something suspect about the posted data, maybe I want to re-show the screen, and not have to re-read the data from a database again?
This is the correct behavior. Only the elements inside form will be posted to your action. Since it is posting the form so your fields should be inside the form in order to get them on your post method.
Update
Also, you cannot read particular field on your action method if you have taken that field readonly on your view. eg: displaying using #Html.LabelFor. In order to get field back on your action use #Html.HiddenFor if field is not to be edited.

How to collect multiple checkbox values using FormCollection?

Based on Darin's answer to my question Ho to display multiple checkbox selection based on user's selection from dropdown?
I am displaying multiple checkboxes based on dropdown selection.
Now, once the user post the form (with multiple inputs) that i have on my page, i collect all the data using FormCollection. And the problem i have is how can i pull those selected checkbox values from formcollection? The number of checkbox will change on different selection from the drop-down, so i think requesting each checkbox value will not work.
Can anyone help me with this problem.
The flow is as shown below:
Properties in Model
public class Subcategory
{
public string Name { get; set; }
public int ID { get; set; }
public bool Flag { get; set; }
}
Displaying PartialView in actual view where other form inputs are there:
<div id="checkboxlist">
#if (Model.SubCategories != null)
{
#Html.Partial("SubCategories", Model.SubCategories)
}
</div>
PartialView SubCategories.cshtml
#model IEnumerable<MyProject.Entities.Subcategory>
#{
// we change the HTML field prefix so that input elements
// such as checkboxes have correct names in order to be able
// to POST the values back
ViewData.TemplateInfo.HtmlFieldPrefix = "checkboxlist";
}
<span>subcategory</span>
<div id="subcategories" style="margin-left: 130px;margin-top: -20px;" data-role="fieldcontain">
<fieldset data-role="controlgroup">
#Html.EditorForModel()
</fieldset>
</div>
EditorTemplates Subcategory.cshtml
#model MyProject.Entities.Subcategory
<div class="editor-label">
#Html.CheckBoxFor(c => c.Flag, new { type = "checkbox" })
<label for="#Model.ID">#Model.Name</label>
#Html.HiddenFor(c => c.Flag)
#Html.HiddenFor(c => c.ID)
#Html.HiddenFor(c => c.Name)
</div>
jquery to display checkboxes based on dropdown selection:
$('#Category').change(function () {
var subcategoriesUrl = $(this).data('subcategoriesurl');
var categoryId = $(this).val();
$('#checkboxlist').load(subcategoriesUrl, { category: categoryId });
});
Don't use FormCollection. That's weakly typed. Use view models. Like this:
[HttpPost]
public ActionResult Foo(MyViewModel model)
{
// model.BusinessSubCategories should contain a list of Subcategory
// where for each element you could use the Flag property to see if
// it was selected or not
...
}
Also notice that you have an inconsistency between the field prefix that you are using in your partial:
ViewData.TemplateInfo.HtmlFieldPrefix = "checkboxlist";
and the view model collection property: Model.BusinessSubCategories. So make sure you fix the prefix to use the correct property name if you want the default model binder to be able to populate this property when you post back.

How i can iterate over items in form Asp.net MVC3

I have a problem in my application
I am using MVC 3 with razor and i want to get the value of checked box in the form
#using (Html.BeginForm("ConfirmItemAvilabilty", "Order", FormMethod.Post, new { id = "frmConfirmAvilabilty", name = "frmConfirmAvilability" }))
{
foreach (OrderItem orderItem in orderAction.Order.OrderItems)
{
<div class="product">
<ul>
<li>
<div class="productImg">
<img src="#orderItem.Product.Image" width="100px" height="100px"/>
</div>
<div class="centered">
Link <span>#orderItem.Product.TitleAr</span>
<span>#orderItem.Product.Price</span>
</div>
#if (currentUser.UserTypeEnum == UserTypeEnum.Reseller)
{
<div>
#Html.CheckBox("ChkConfirm", orderItem.IsAvailable, new { id="chkConfirm" ,#class="chkConfirm"})
#Html.Hidden("OrderItemId", orderItem.Id, new { id="hdnConfirm"})
</div>
}
</li>
</ul>
</div>
}
if (currentUser.UserTypeEnum == UserTypeEnum.Reseller)
{
<button>Confirm</button>
}
}
Simply i want to get the value of all checked checkboxs i tryed to create a model holding the value of my checkbox and the value of the hidden text below it
public class ItemOrderModel
{
public string ChkConfirm { get; set; }
public string OrderItemId { get; set; }
}
and in my controller i do the following but nothing happened
public ActionResult ConfirmItemAvilabilty(List<ItemOrderModel> OrderItems)
{
return View();
}
but orderItems always returns null, Can anyone help me in that?
----------------- Edit ------------------
Thank you Sam and Jesse
I Found a solution for my problem but I am facing another problem now first of all i solved my problem by changing in model view to be like that
public class ItemOrderModel
{
public List<bool> ChkConfirms { get; set; }
public List<string> OrderItemId { get; set; }
}
and change the checkbox name to be
#Html.CheckBox("ChkConfirms", orderItem.IsAvailable, new { id = "chkConfirm", #class = "chkConfirm" })
the problem now is
when i submit i found two values false that is the actual representation of my checkboxs and two ids that's also the actual representation of my hidden fields "Correct scenario"
when i check one of check boxs in the same form i found 3 result for the check box and 2 results for the hidden field Can any one help in that or have a solution
You need to look into Editor Templates:
How to create custom editor/display templates in ASP.NET MVC 3?
These allow you to do the exact thing you are talking about.

Categories

Resources