Exclude <input type='file'> from ViewModel - asp.net-mvc

I have the following file input tag in the "Create" View:
<input type="file" id="RequestFile" name="RequestFile"/>
#Html.ValidationMessage("RequestFile")
The ViewModel contains this corresponding property:
[Required(ErrorMessage="Please select a file")]
public HttpPostedFileBase RequestFile { get; set; }
This works fine in the "Create" View, but in the "Edit" View, I get ModelState.Isvalid as false. Using the same ViewModel I would like to exclude this field from validations because I would not want to upload the file again.
I tried simply disabling the input tag like this:
<input type="file" id="RequestFile" name="RequestFile" disabled/>
This has a disabled input control but the Validation still fired.
Also applying the BindAttribute in the Controller did not work (see this Question)
Ideally (I know it sounds unlikely), if there is a server-side solution to this, please post your thoughts. If there is a small client-side trick, please let me know!

The best ways are remove the property altogether, and always access it directly from the form collection (and manually validate it) or manually remove the model state error using the property name (as #cheesemacfly has in his comment, ModelState.Remove("RequestFile")). The latter makes it very easy to fix then.

You could use form.onsubmit to check to see it document.getElementById("RequestFile").value is not null/empty and cancel the submit if it is.
something like
<form onsubmit="if(!document.getElementById('RequestFile').value){alert('Please select a file.');return false;}" >
<input type="file" id="RequestFile" name="RequestFile" />
<input type="submit"/>
</form>
return false cancels the submission.
http://jsfiddle.net/Cg7HY/1/
or put it in the click event of the submit button itself
http://jsfiddle.net/Cg7HY/3/

Related

Dynamic Form in ASP.NET MVC3 Razor

I have a small problem.
I want to have dropdown list with some objects. After clicking on one I want to add it to list with textfield for naming it. I don't want to limit quantity of this fields. I want to receive in controller ID (stored in dropdown list) and name (given by user) for each selected item. How can I do it?
I was thinking about storing it in some fields as a text, and parsing in cotroller but I think it's not elegant.
EDIT.
Ok, Thansk for your help, but it's not working for me correctly.
I generate html like this:
<input type="hidden" value="96" name="Inputs[0].Key">
<input type="text" name="Inputs[0].Value">
In my controller I'm receiving this dictionary. The problem is that quantity of elements is correct, but all values are null. What is wrong here?
The best way to go about this is by using array-style model binding.
So, for each element you wish to name you create a hidden field to store the drop down value plus a text field to store the user-given name. You name them as follows:
<input type="hidden" name="element[0].Key" /><input type="text" name="name[0].Value" />
increasing the index value each time. This is easily achieved with a bit of JavaScript. You then create an action method which takes a KeyValuePair<string, string>[] as a parameter. You will then be able to parse through your values no problem with a loop or LINQ expression.
Use IEnumerable<KeyPairValue<string,string>> MySelectedItem = new List<KeyPairValue<string,string>>(); on model, and when adding it to the list, name it like an array:
MySelectedItem[1].Key, MySelectedItem[1].Value, MySelectedItem[2].Key...
(I haven't tested this, but it should work)
Edit: check out this blog post with better explanation on how to do it: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

Why is MVC persisting on HttpGet?

I thought I understood MVC until now.
To me a GET should be from a clean slate. But I discoved today MVC assumes a GET to be a POST if a page request a get from itself.
The text box should always show the text "Red" but instead it persist its last value from the previous view.
Acting like an HTTPPost. You have to uncomment ModelState.Clear to act like a HttpGet.
This appears to me to be a bug.
<form action="" method="get">
<div>
<%=Html.TextBox("search") %>
<input type="submit" value="Search" />
</div>
</form>
[HttpGet]
public ActionResult Index(string search)
{
//ModelState.Clear();
ViewData["search"] = "Red";
var items = GetYourTestData;
if (!string.IsNullOrEmpty(search))
{
var items2 = items.Where(x => x.Color == search).ToList();
return View(items2);
}
return View(items);
}
The search results returns correct and different data so it is not browser cache.
For the purpose of a search results page it does not make sense to have to redirect to another page to avoid this. That is why I chose GET thinking it should be clean each time.
Like I stated in the description. Other contents on the page does change so it is not cache. Uncomment ModelState.Clear() and all is good so that is not cache.
You can put a dynamic datetime label always showing the latest time from the server and it does change. That also proves it is not page cache.
It is a very simple test to do. Yes, just a sure as there is gravity, MVC2 framework 4.0 considers it an HTTPPost if the HttpGet requested page is the same as the requester. If you are not aware of this during programming the results could be disastrous. For instance, If someone like TurboTax uses MVC I hope that their developer are aware of that.
... ViewData["AdjustedTaxAmount"]=3435.00; ... is not going to work unless they call this
ModelState.Clear().
I don't know of why there should be ModelState on a get so
one sure fire work around is to
Inherit Controller from a base class
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
ModelState.Clear();
}
base.OnActionExecuted(filterContext);
}
The browser will cache GET requests that are from the same URL. If you add a querystring variable that is random then the GET request will be different each time and the browser will not cache the result.
That's to be expected.
When you submit a form via GET, you're serializing its elements and passing those to the target via the QueryString. So, it makes sense that your search value would be part of MVC's model in the next request. When you use the Html.TextBox helper, it's going to automatically inject the model's value as the HTML input's value. That's why you're seeing the value cover over.
One solution to avoid that is to not use the HTML Helper to render the input:
<form action="" method="get">
<div>
<input type="text" name="search" />
<input type="submit" value="Search" />
</div>
</form>
Then, you should get the clean slate you're expecting after each form submission.

Why is my form triggering the wrong controller action?

This form has multiple submit buttons, when clicked, it calls a simple JavaScript function to change the value of a hidden input (function is called "setHidden". This worked before, after some other not relevant code, it has ceased working.
Essentially, the action it is supposed to call is never called, instead it seems to default back to a previous URL.
The Form:
<form action="/League/RemoveOwner" method="post">
<input type="hidden" value="1007" name="lid"/>
<input type="hidden" value="0" id="index" name="index"/>
<input type="image" src="../../Resources/Images/Delete.png"
height="12" alt="Remove Owner" title="Remove Owner"
onclick="setHidden('index', '1031')"/></a> coach<br />
</form>
The Controller:
[HttpPost]
public ActionResult RemoveOwner(int id, string index)
{
//yada
return PartialView();
}
When clicking the image, it should call the remove owner controller, instead it calls the "View" controller:
public ActionResult View(int id) {
//yada
return View();
}
After searching high and low, I finally found the problem. There was another form on the page that didn't have a closing tag. THAT form was supposed to take it back to the "View" controller. It was on a partial, so I didn't catch it until... well until I had racked my brain for 2 hours...
Another suggestion for you form action code is don't write the absolute path for your form action. You could use the Html.BeginForm() or write your site url in the web.config and fetch it to the Application["URL"] while the application start at global.ascx. Then your form should look like:
<form action="<%=Application["URL"]%>League/RemoveOwner">
This approach can avoid some errors while publishing to the IIS or another web server.

asp.net MVC checkbox headache!

I have seen lots of questions relating to this topic.
I am using asp.net MVC 1.0
Problem area
If I use
<%= Html.CheckBox("Status", true) %>
Then why It renders like
<input checked="checked" id="Status" name="Status" type="checkbox" value="true" /><input name="Status" type="hidden" value="false" />
I put this in foreach loop and I have 5 rows.
when I submit form with true,true,true,false,false
then I get true,false,true,false,true,false,false,false
i.e. for false => false.
for true => true,false
If I use
<input type="checkbox" value="true" name="Status" checked="checked" />
Then I don't get unchecked one's.
so how do I overcome form this problem?
Please don't post answer with using loop in formcollection object and checking each key!
I know this isn't the elegant one but this is what I did:
collection["response"].Replace("true,false", "true").Split(',').ToList();
In your example, when you submit form with true,true,true,false,false and you get
true,false,true,false,true,false,false,falseit is interesting to note that you are not actually getting eight values back, but five arrays that merely looks like this is the case because all of the values are joined.
I know you asked to not get a loop for your answer, but I can use one to demonstrate what is really happening here:
foreach (string key in postedForm.AllKeys) {
// "value" will contain a joined/comma-separated list of ALL values,
// including something like "true,false" for a checked checkbox.
string value = postedForm[key].GetValue;
// "values" will be an array, where you can access its first element,
// e.g., values[0], to get the actual intended value.
string[] values = postedForm.GetValues(key);
}
So, for your checked boxes, you'll get a values array with two elements, and for unchecked boxes, you'll get just a single-element array.
Thus, to answer your question how do you overcome this problem, the answer lies in using GetValues rather than GetValue, and thinking of your posted fields as arrays rather than strings.
Best of luck!
Personally I think having to check for "true,false" everywhere on the server is a pain. I wrote a jquery fix that will remove the extra hidden field created by the Html.Checkbox helper when a box is checked, then add it back if the box is unchecked. Server values will always be "true" or "false". Checkbox lists are kind of subjective in how you want them to act, which I discuss, but I'm removing "false" from the value set, which means the form value will be excluded if all boxes in the list are unchecked.
http://www.mindstorminteractive.com/blog/?p=386
I've had pretty good success using this technique. Please let me know if you try it out and have issues.
You'll have to do your own model binding for the CheckBox values.
Get the list of values from the FormCollection or Request.Form for that CheckBox id and replace true,false with true:
string chkBoxString = Request.Form["checkboxID"].Replace("true,false", "true")
Now you have a list of whether a CheckBox was selected or not.... do the rest yourself :)
It renders so because default binder requires the FormCollection to have a value for nonnullable parameters. Using this technique we are sure that the value will be sent even the checkbox is not checked (by default the value sent only when it's checked). If you use this controller method with just one html input you'll get error on form post with unchecked checkbox (value of checkbox will not be posted and binder will not know what to use for value of isItemSelected):
public ActionResult SomeActionMethod(bool isItemSelected)
You can try use something like this with just one html input:
public ActionResult SomeActionMethod(bool? isItemSelected)
But in this case isItemSelected will be null or will be true. And it will never become false.
Well there are couple of ways you can do based on your requirement.
I use this method.
<input type="checkbox" value="<%= item.ID %>" name="check" checked="checked")" />
This is may checkboxes.
On server side I will also have array of ID's of item in the model.
So I check it whether it is in array
var strArray = form["checkbox"]; //Request.form["checkbox"] or "FormCollection form" in action parameter; array of ID's in comma separated string.
Different people have different tests.
this was intended to use for just just simple CheckBox, what you want is checkboxList, which is not yet cover in the API of ASP.net MVC
If you looking for some thing like checkboxlist, maybe you should write your own helper, provide you understand HTML well..
That's it! :)
Easier just to check whether AttemptedValue.Contains("true") - it will, if it's checked, not if it's unchecked....
in the View :
<input id="radio5" type="checkbox" name="rb_abc" value="5"/>
Controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult YourForm(FormCollection fc)
{
if (fc["rb_abc"] == "on")
{
//Hey .. you have selected a Radio Button.. just kidding.. its a CheckBox
}
}
To get checkbox value even it is true or false
var boolValue = bool.Parse(collection.GetValues("checkboxID")[0])

DropDownList in conjunction with file upload in asp.net mvc

I have a file upload function in my asp.net mvc application that allows users to upload an xslx file containing data that should be persisted to a database. That data can be related to one of many categories. I need to be able to discern which category the data that is coming in is supposed to be related to, so I thought that a drop down list would be perfect for the job. However, I don't know how to get at the lists selected value when the user posts the data. This is what the code for the form looks like:
<form action="/Import/UploadFiles/" method="post" enctype="multipart/form-data">
<fieldset id="fileImport">
<legend>Importinställningar</legend>
<label for="file">Importfil:</label>
<input type="file" id="file" name="file" />
<%= Html.DropDownList("Name", (IEnumerable<SelectListItem>)ViewData["assignments"]) %>
<p>
<input type="submit" value="Spara"/>
<input type="button" value="Avbryt" onclick="window.location.href='/'" />
</p>
</fieldset>
</form>
Since I am dealing with a file upload scenario I don't have an action link that I can use to pass data to the controller, but rather an input with the type submit.
How should I go about reading the select value of the drop down list so that its selected value can be passed to the Controller?
There are a couple of different ways that you can make this work. First, add a string parameter named Name to your UploadFiles method. The default binder will fill it in from the form value with the same name. Alternatively, you can use the ValueProvider inside the controller -- if you use the same action for both rendering the view and responding to the post, for instance -- to extract the value of the parameter named Name.
public ActionResult UploadFiles( string Name )
{
...
}
or
public ActionResult UploadFiles()
{
string name = this.ValueProvider.ContainsKey("Name")
? this.ValueProvider[key].AttemptedValue
: null;
...
}

Resources