Why does this viewmodel value not render correctly? - asp.net

In my UploadFilesViewModel I have a property:
[HiddenInput(DisplayValue = false)]
public bool DirBlocked { get; set; }
and in my view, the following markup for it:
#Html.HiddenFor(m => m.DirBlocked)
When I execute the following code in my GET action:
var model = new UploadFilesViewModel { UserBaseDir = await GetUserBaseDirAsync() };
model.DirBlocked = true;
return View(model);
The hidden input for DirBlocked renders as follows:
<input data-val="true" data-val-required="The DirBlocked field is required." id="DirBlocked" name="DirBlocked" type="hidden" value="True">
Yet when I execute the following code in the POST action:
// Hard 'true' just for debugging.
//if (files.Any() && !HasDirAccess(model.UploadDir))
if (true)
{
model.DirBlocked = true;
return View(model);
}
The same hidden input renders as follows:
<input data-val="true" data-val-required="The DirBlocked field is required." id="DirBlocked" name="DirBlocked" type="hidden" value="False">
That is, it loses the true value assigned to the DirBlocked property. What could be causing this? Normally when I do a return View(model) in a POST action all model properties are rendered correctly, as they are set.

When re-displaying a posted page, the Html Helper-methods will first look for the posted value to re-display the form. If they can't find a value, they will take it from the model object instead. What you can do to prevent that is to clear the ModelState before doing a return:
model.DirBlocked = true;
ModelState.Clear();
return View(model);
That will, however, clear the form of any user-entered data not passed back to the view, and it will also clear any validation messages. See https://msdn.microsoft.com/en-us/library/system.web.mvc.modelstatedictionary.aspx for more information about the ModelState.

Actually the HtmlHelper does not use Model but ModelState. You can solve this by avoiding the html helper and using the model field directly.

Related

Set and Get the default value of checkbox in ASP.NET mvc

I am using Checkbox in my ASP.Net MVC project,
I can set check box by default as unchecked.
Age:
<input class="associationCheckbox" type="checkbox" name="age" />
When I post the form (For has only checkbox), the checkbox is not showing in formCollection (Here expected name and value are age, ‘false’).
Using below code,I was reading the velues.
[HttpPost]
public ActionResult Index(FormCollection formCollection)
{
foreach (var key in formCollection.Keys)
{
var value = formCollection[key.ToString()];
}
return RedirectToAction("CorticonIndex", "Test");
}
If I check the check box at run time, then submit the form.
I can get the name and value.
How to get the default values(false)If I don’t check checkbox?
I tried setting value property using model property but same issue is coming.
Model Class:
public bool CheckBoxvalue { get; set; }
this.CheckBoxvalue=false
Age: <input class="associationCheckbox" type="checkbox" name="age" value=#Model.CheckBoxvalue />
Can someone suggest me how to set and get default value.
I had look at below post from stack overflow but didn't info for my requirement.
How to set a CheckBox by default Checked in ASp.Net MVC
http://www.tutorialsteacher.com/mvc/htmlhelper-checkbox-checkboxfor
Thanks,
Balu
You can do something like this:
Age: <input class="associationCheckbox" type="checkbox" name="age" #(Model.CheckBoxvalue ? "checked" : "") value=#Model.CheckBoxvalue />
But it is always good to use HTMLHelper methods if you are using razor

Asp.net mvc3 razor with multiple submit buttons

I'm using MVC3 Razor. I have 2 submit buttons setup on my view but the problem I'm having is that both submit buttons cause the validation of the model. I want to hook up individual submit buttons with specific input controls for validation.
I know this is a few months old but the solutions here seemed needlessly complex and there's no accepted answer yet. If you name your inputs the same but give them different values, you can get that value in your controller just by including a string with the name of the input as a variable. This is how I solved this problem:
View:
<input type="submit" id="EnterprisePush" name="btnSubmit" value="Push" />
<input type="submit" id="EnterprisePull" name="btnSubmit" value="Pull" />
Controller:
[HttpPost]
public ActionResult EnterpriseAdmin(int id, string btnSubmit, FormCollection collection)
{
switch (btnSubmit) {
case "Push":
/* Do Something here */
break;
case "Pull":
/* Do Something else here */
break;
}
The browser is always going to submit the entire form regardless of which submit button you press.
The best solution would be to have two submit buttons with the same value for the name attribute and different values for the value attributes.
When you submit the form, the value of the button will be submitted as well. In your action which handles that form submission, you check to see the value of the button and perform the correct validation based on that.
In your form you would have something like this:
<button type="submit" name="Command" value="command1">Do Command #1</button>
<button type="submit" name="Command" value="command2">Do Command #2</button>
Your Form Model would look like this:
public class MyFormModel() {
public string Command {get;set;}
public string SomeOtherVal {get;set;}
}
Your controller\action would look like this:
public ActionResult HandleFormSubmit(MyFormModel model) {
if (model.Command == "command1") {
// do something
} else if (model.Command == "command2") {
// do something else
}
}
Firstly, you can disable client validation on your cancel button simply by adding the CSS class 'cancel' to it. See: Disable client-side validation in MVC 3 "cancel" submit button
Secondly, as well testing the submit element's form name as described above, you can use a custom action selector. Here's mine, which I originally took from the blog post shown in the comment:
/// <summary>
/// Used to vary an action method based on which button in a form was pressed. This
/// is useful but is an anti-pattern because it couples the controller to names
/// used in the form elements.
/// </summary>
/// <remarks>
/// See the example at http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-same-form.aspx
/// </remarks>
public class AcceptButtonAttribute : ActionMethodSelectorAttribute
{
public string ButtonName { get; set; }
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
var req = controllerContext.RequestContext.HttpContext.Request;
return !string.IsNullOrEmpty(req.Form[this.ButtonName]);
}
}
In your controller:
[HttpPost]
[ActionName("Edit")]
[AcceptButton(ButtonName = "Cancel")]
public ActionResult Edit_Cancel(MyModel model)
{
return RedirectToAction("Index");
}
[HttpPost]
[AcceptButton(ButtonName = "Save")]
public ActionResult Edit(MyModel model)
{
// do real work here
}
Note that you need the [ActionName("Edit")] attribute to tell MVC that although using a different method name, it is for the Edit action.
And in your View:
<input type="submit" name="Save" value="Save" />
<input type="submit" name="Cancel" value="Cancel" class="cancel" />
My solution was to do two things. Say we have a Save button and another Add Something button. When user clicks on Save we want client validation and server validation to be performed. For later button we don't want any validation to take place.
Temporarily disable client validation for second button (on click):
<input type="submit" name="submit-button" value="Save" />
<input type="submit" name="submit-button" value="Add Something" onclick="document.forms[0].noValidate = true; document.forms[0].submit();" />
Good thing about it is when JavaScript is disabled the client validation would never have taken place anyway.
Take care of server side
Similar to what Bryan is saying when you click any submit button within a form, the entire form and the clicked submit button value is posted. You can differentiate which button was clicked by the name posted. In example above when user clicks on Save button and we read Request.Form["submit-button"] in controller post action we get "Save". If user clicked on Add Something we would get "Add Something". This is the way HTML is supposed to work.
Now to get around having magic strings all over the place I usually have a public static class within the controller, like so:
public class HomeController
{
public static class Buttons
{
public const string Save = "Save";
public const string AddSomething = "Add something";
}
// Action methods
}
So you can use these for rendering form:
<input type="submit" name="submit-button" value="#HomeController.Buttons.Save" />
And you can easily read the button clicked in controller:
[HttpPost]
public ActionResult Index(Model viewModel)
{
var buttonClicked = Request.Form["submit-button"];
switch (buttonClicked) {
case HomeController.Buttons.Save:
return Save(viewModel);
case HomeController.Buttons.AddSomething:
return AddSOmething(viewModel);
}
return View();
}
In Save method you first ask if ModelState.IsValid and return view model if not but in AddSomething method we will clear any errors:
public ActionResult AddSomething(Model viewModel)
{
ModelState.Clear();
// your code to add something to model
return View(viewModel);
}
This was you keep everything clean, tidy and testable. And you can introduce a constant for submit-button html name attribute. It might be possible to do all the constants with T4MVC too. A similar solution applies to when you need a "auto postback" combo box, except you need a hidden field that is set via onchange event of the select element.
Hope this helps.
Just use this code as a template:
#{
var nextButtonVal = "Next >>";
var backButtonVal = "<< Back";
if (IsPost) {
if(Request["navigate"].Equals(backButtonVal)){Response.Redirect("~/pageFoo");}
if(Request["navigate"].Equals(nextButtonVal)){Response.Redirect("~/pagebar");}
}
}
<input type="submit" value="#backButtonVal" title="Back" name="navigate"/>
<input type="submit" value="#nextButtonVal" title="Next" name="navigate"/>
One final thing I would do is instead of using intelligent strings, use an enum to determine the value for each input tag. Using razor syntax:
#Enum.GetName(typeof(YourEnumType), yourEnum.WhateverValue)
then in your controller:
public ActionResult DoSomethingBasedOnEnumValue(string enumValue)
{
YourEnumType localVar = (YourEnumType)Enum.Parse(typeof(YourEnumType), enumValue);
switch(localVar)
{
case YourEnumType.Action1:
//do something
break;
case YourEnumType.Action2:
//do something else
break;
}
return View();
}
If you want to have separate action for delete, try this.
add a delete action in the controller and mark it as HttpDelete,
[HttpDelete]
public ActionResult Edit(int id, string foo) {
...
}
And in the view,
button name should be X-HTTP-Method-Override and value should be DELETE
<button name="X-HTTP-Method-Override" value="DELETE" formnovalidate="formnovalidate" class="cancel">Delete</button>
note: all most all the browsers don't allow for other HTTP methods, like HEAD, PUT, or DELETE. but by add a header to the HTTP request, X-HTTP-Method-Override, that is supposed to be interpreted by the service and acted upon regardless of the actual HTTP method used. So above code will add a header to the request like X-HTTP-Method-Override: DELETE. and .net framework will do the rest of the things and direct you to delete action.
Submit button name don't come to server side if in all from this situation you are will be use [Remote] attribute for validation model property.

How to retrieve form values from HTTPPOST, dictionary or?

I have an MVC controller that has this Action Method:
[HttpPost]
public ActionResult SubmitAction()
{
// Get Post Params Here
... return something ...
}
The form is a non-trivial form with a simple textbox.
Question
How I access the parameter values?
I am not posting from a View, the post is coming externally. I'm assuming there is a collection of key/value pairs I have access to.
I tried Request.Params.Get("simpleTextBox"); but it returns error "Sorry, an error occurred while processing your request.".
You could have your controller action take an object which would reflect the form input names and the default model binder will automatically create this object for you:
[HttpPost]
public ActionResult SubmitAction(SomeModel model)
{
var value1 = model.SimpleProp1;
var value2 = model.SimpleProp2;
var value3 = model.ComplexProp1.SimpleProp1;
...
... return something ...
}
Another (obviously uglier) way is:
[HttpPost]
public ActionResult SubmitAction()
{
var value1 = Request["SimpleProp1"];
var value2 = Request["SimpleProp2"];
var value3 = Request["ComplexProp1.SimpleProp1"];
...
... return something ...
}
Simply, you can use FormCollection like:
[HttpPost]
public ActionResult SubmitAction(FormCollection collection)
{
// Get Post Params Here
string var1 = collection["var1"];
}
You can also use a class, that is mapped with Form values, and asp.net mvc engine automagically fills it:
//Defined in another file
class MyForm
{
public string var1 { get; set; }
}
[HttpPost]
public ActionResult SubmitAction(MyForm form)
{
string var1 = form1.Var1;
}
The answers are very good but there is another way in the latest release of MVC and .NET that I really like to use, instead of the "old school" FormCollection and Request keys.
Consider a HTML snippet contained within a form tag that either does an AJAX or FORM POST.
<input type="hidden" name="TrackingID"
<input type="text" name="FirstName" id="firstnametext" />
<input type="checkbox" name="IsLegal" value="Do you accept terms and conditions?" />
Your controller will actually parse the form data and try to deliver it to you as parameters of the defined type. I included checkbox because it is a tricky one. It returns text "on" if checked and null if not checked. The requirement though is that these defined variables MUST exists (unless nullable(remember though that string is nullable)) otherwise the AJAX or POST back will fail.
[HttpPost]
public ActionResult PostBack(int TrackingID, string FirstName, string IsLegal){
MyData.SaveRequest(TrackingID,FirstName, IsLegal == null ? false : true);
}
You can also post back a model without using any razor helpers. I have come across that this is needed some times.
public Class HomeModel
{
public int HouseNumber { get; set; }
public string StreetAddress { get; set; }
}
The HTML markup will simply be ...
<input type="text" name="variableName.HouseNumber" id="whateverid" >
and your controller(Razor Engine) will intercept the Form Variable "variableName" (name is as you like but keep it consistent) and try to build it up and cast it to MyModel.
[HttpPost]
public ActionResult PostBack(HomeModel variableName){
postBack.HouseNumber; //The value user entered
postBack.StreetAddress; //the default value of NULL.
}
When a controller is expecting a Model (in this case HomeModel) you do not have to define ALL the fields as the parser will just leave them at default, usually NULL. The nice thing is you can mix and match various models on the Mark-up and the post back parse will populate as much as possible. You do not need to define a model on the page or use any helpers.
TIP: The name of the parameter in the controller is the name defined
in the HTML mark-up "name=" not the name of the Model but the name of the expected variable in the !
Using List<> is bit more complex in its mark-up.
<input type="text" name="variableNameHere[0].HouseNumber" id="id" value="0">
<input type="text" name="variableNameHere[1].HouseNumber" id="whateverid-x" value="1">
<input type="text" name="variableNameHere[2].HouseNumber" value="2">
<input type="text" name="variableNameHere[3].HouseNumber" id="whateverid22" value="3">
Index on List<> MUST always be zero based and sequential. 0,1,2,3.
[HttpPost]
public ActionResult PostBack(List<HomeModel> variableNameHere){
int counter = MyHomes.Count()
foreach(var home in MyHomes)
{ ... }
}
Using IEnumerable<> for non zero based and non sequential indices post back. We need to add an extra hidden input to help the binder.
<input type="hidden" name="variableNameHere.Index" value="278">
<input type="text" name="variableNameHere[278].HouseNumber" id="id" value="3">
<input type="hidden" name="variableNameHere.Index" value="99976">
<input type="text" name="variableNameHere[99976].HouseNumber" id="id3" value="4">
<input type="hidden" name="variableNameHere.Index" value="777">
<input type="text" name="variableNameHere[777].HouseNumber" id="id23" value="5">
And the code just needs to use IEnumerable and call ToList()
[HttpPost]
public ActionResult PostBack(IEnumerable<MyModel> variableNameHere){
int counter = variableNameHere.ToList().Count()
foreach(var home in variableNameHere)
{ ... }
}
It is recommended to use a single Model or a ViewModel (Model contianing other models to create a complex 'View' Model) per page. Mixing and matching as proposed could be considered bad practice, but as long as it works and is readable its not BAD. It does however, demonstrate the power and flexiblity of the Razor engine.
So if you need to drop in something arbitrary or override another value from a Razor helper, or just do not feel like making your own helpers, for a single form that uses some unusual combination of data, you can quickly use these methods to accept extra data.
If you want to get the form data directly from Http request, without any model bindings or FormCollection you can use this:
[HttpPost]
public ActionResult SubmitAction() {
// This will return an string array of all keys in the form.
// NOTE: you specify the keys in form by the name attributes e.g:
// <input name="this is the key" value="some value" type="test" />
var keys = Request.Form.AllKeys;
// This will return the value for the keys.
var value1 = Request.Form.Get(keys[0]);
var value2 = Request.Form.Get(keys[1]);
}

Html.ValidationMessage renders on all fields, regardless if they're valid or not

My model is correctly validated. If I take a peak in the validation results during debug, I will see that everything is correct. However, all my validation results will show, even if only one is invalid. Again, during debug, only one field is correctly showing up in the validation results, but when my view is rendered all our displayed:
[HttpPost]
public ActionResult Create(Widget widget)
{
if (widge.Valid)
{
// Save to db
}
retun View(widget);
}
My view:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" Inherits="System.Web.Mvc.ViewPage<Widget>" %>
// took out a lot of html here
<form action="Create" method="post">
<input name="Widget.City" value="<%= Model.City == null ? "" : Model.City%>" />
<%= Html.ValidationMessage("Widget.City")%>
<input name="Widget.Department" value="<%= Model.Department == null ? "" : Model.Department %>" />
<%= Html.ValidationMessage("Widget.Department")%>
<button type="submit">Save</button>
</form>
Let us say City and Department are set to NotNull in my model and I correctly put in a City, but leave Department blank. Again, it will show it is invalid on the controller, with the property Department having a problem, yet in my view I'll get "may not be null" messages for both properties. If I have 4 properties and 4 ValidationMessage tags in my view, even if one property is not valid ... all 4 will show. What's the deal?
If I'm not mistaken, I think you want to be using
Html.ValidationMessageFor(model => model.City)
Not what you're using currently in your view.
Also... Since you're using a strongly typed view, you should be checking ModelState.IsValid to determine whether or not you should save your Widget. That is if you're using data annotations on your view model.
What are you using as your input to the [HttpGet] action of the view?
Since you're using the format Widget.Property the view expects the model to have a Widget property containing the data for the widget.. i.e it is looking for Model.Widget.Property but your model only contains Model.Property. Based on the code posted here, you're only passing the widget back to the view and it will interpret that as having a NULL Widget property, thus triggering all NotNull validation.
A solution here is to assign the Widget post model (what you're accepting in the [HttpPost] action) to a Widget property of the of the model you're passing back to the view.
public class CreateViewModel
{
public Widget Widget { get; set; }
}
[HttpPost]
public ActionResult Create(Widget widget)
{
if (widge.Valid)
{
// Save to db
}
var viewModel = new CreateModel() { Widget = widget };
retun View( viewModel );
}
Hopefully I articulated that correctly.. Its been a long day = )

Why does the CheckBoxFor render an additional input tag, and how can I get the value using the FormCollection?

In my ASP.NET MVC app, I am rendering a checkbox using the following code:
<%= Html.CheckBoxFor(i=>i.ReceiveRSVPNotifications) %>
Now, I see that this renders both the checkbox input tag and a hidden input tag. The problem that I am having is when I try retrieve the value from the checkbox using the FormCollection:
FormValues["ReceiveRSVPNotifications"]
I get the value "true,false". When looking at the rendered HTML, I can see the following:
<input id="ReceiveRSVPNotifications" name="ReceiveRSVPNotifications" value="true" type="checkbox">
<input name="ReceiveRSVPNotifications" value="false" type="hidden">
So the FormValues collection seems to join these two values since they have the same name.
Any Ideas?
Have a look here:
http://forums.asp.net/t/1314753.aspx
This isn't a bug, and is in fact the same approach that both Ruby on
Rails and MonoRail use.
When you submit a form with a checkbox, the value is only posted if
the checkbox is checked. So, if you leave the checkbox unchecked then
nothing will be sent to the server when in many situations you would
want false to be sent instead. As the hidden input has the same name
as the checkbox, then if the checkbox is unchecked you'll still get a
'false' sent to the server.
When the checkbox is checked, the ModelBinder will automatically take
care of extracting the 'true' from the 'true,false'
I had the same problem as Shawn (above). This approach might be great for POST, but it really sucks for GET. Therefore I implemented a simple Html extension that just whips out the hidden field.
public static MvcHtmlString BasicCheckBoxFor<T>(this HtmlHelper<T> html,
Expression<Func<T, bool>> expression,
object htmlAttributes = null)
{
var result = html.CheckBoxFor(expression).ToString();
const string pattern = #"<input name=""[^""]+"" type=""hidden"" value=""false"" />";
var single = Regex.Replace(result, pattern, "");
return MvcHtmlString.Create(single);
}
The problem I now have is that I don't want a change to the MVC framework to break my code. So I have to ensure I have test coverage explaining this new contract.
I use this alternative method to render the checkboxes for GET forms:
/// <summary>
/// Renders checkbox as one input (normal Html.CheckBoxFor renders two inputs: checkbox and hidden)
/// </summary>
public static MvcHtmlString BasicCheckBoxFor<T>(this HtmlHelper<T> html, Expression<Func<T, bool>> expression, object htmlAttributes = null)
{
var tag = new TagBuilder("input");
tag.Attributes["type"] = "checkbox";
tag.Attributes["id"] = html.IdFor(expression).ToString();
tag.Attributes["name"] = html.NameFor(expression).ToString();
tag.Attributes["value"] = "true";
// set the "checked" attribute if true
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
if (metadata.Model != null)
{
bool modelChecked;
if (Boolean.TryParse(metadata.Model.ToString(), out modelChecked))
{
if (modelChecked)
{
tag.Attributes["checked"] = "checked";
}
}
}
// merge custom attributes
tag.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
var tagString = tag.ToString(TagRenderMode.SelfClosing);
return MvcHtmlString.Create(tagString);
}
It's similar to Chris Kemp's method, which is working fine, except this one does not use the underlying CheckBoxFor and Regex.Replace. It's based on the source of the original Html.CheckBoxFor method.
I think that the simplest solution is to render the INPUT element directly as follows:
<input type="checkbox"
id="<%=Html.IdFor(i => i.ReceiveRSVPNotifications)%>"
name="<%=Html.NameFor(i => i.ReceiveRSVPNotifications)%>"
value="true"
checked="<%=Model.ReceiveRSVPNotifications ? "checked" : String.Empty %>" />
In Razor syntax it is even easier, because the 'checked' attribute is directly rendered with a "checked" value when given a 'true' server-side value.
Here is the source code for the additional input tag. Microsoft was kind enough to include comments that address this precisely.
if (inputType == InputType.CheckBox)
{
// Render an additional <input type="hidden".../> for checkboxes. This
// addresses scenarios where unchecked checkboxes are not sent in the request.
// Sending a hidden input makes it possible to know that the checkbox was present
// on the page when the request was submitted.
StringBuilder inputItemBuilder = new StringBuilder();
inputItemBuilder.Append(tagBuilder.ToString(TagRenderMode.SelfClosing));
TagBuilder hiddenInput = new TagBuilder("input");
hiddenInput.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
hiddenInput.MergeAttribute("name", fullName);
hiddenInput.MergeAttribute("value", "false");
inputItemBuilder.Append(hiddenInput.ToString(TagRenderMode.SelfClosing));
return MvcHtmlString.Create(inputItemBuilder.ToString());
}

Resources