I am using ASP.NET MVC4 with .NET Framework 4.5. I have a controller action that accepts a model of one type with a property named 'Name' but renders a view using a model of another type. I am still able to use #Html.TextBox("Name") and #Html.ValidationMessage("Name").
I want the textbox to display the sanitized input, that is, the input without leading/trailing/extra spaces the user may have entered. The setter for my model sanitizes the value for me, and I am successfully obtaining the sanitized value using the getter within the controller action. It's just that upon submitting the form, the textbox still displays the unclean input.
Is there some mechanism I am missing? Is the #Html.TextBox(string name) helper looking at the raw request data and not the model? If so, how come the validation message is working?
Update
I have just tried defining a new view model that includes my textbox field so I could hopefully just use the #Html.TextBoxFor helper. Everything is still working as it was after a re-build, I am still not getting sanitized input appearing in the textbox. I still don't know a solution for this.
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult MyAction(MyViewModel model)
{
if (this.ModelState.IsValid)
{
using (var service = new MyService())
{
model.MyResults = service.DoSomething(model.MySanitizedProperty);
}
}
return this.View("MyView", model);
}
Then, in "MyView":
#Html.TextBoxFor(m => m.MySanitizedProperty)
<input type="submit" value="Go" />
#Html.ValidationMessageFor(m => m.MySanitizedProperty)
In the controller, invoking model.MySanitizedProperty returns the sanitized value while the textbox goes on to display the unsanitized data.
It sounds like a problem with the models; make sure you are properly accessing the value from the model you wish to populate it with, i.e., possibly discretely specifying the model "Name" is coming from.
Also, check to see that the setter has a chance to operate on the value - if the controller is activating before the setter function is used, then you'll only get the original input value.
Realize you have to go to the server for the setter to work, possibly you need a async postback or such, and the value reloaded.
Related
So this is a bit of a 'how can I be lazier?' or 'can I be super overprotective?' type of question. The scenario I'm trying to account for is this...
A model is updated with a new field, but for whatever reason the addition of the property in the model's update user interface is forgotten while the explicit setting of it is implemented in the db access controller. When the user clicks submit, the value of this property makes it to the controller as 'null' and summarily the database value is 'updated' to null.
Is there a way to get the properties on the model that are not explicitly put into the form, and add hiddenfors for these properties? (Worst case scenario, this value is not updated as opposed to losing data).
Edit: Potential Scenario
Initial object (used as the model on the form)
public MyObject
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
The form has
#Html.TextboxFor(model => model.Value1)
#Html.TextboxFor(model => model.Value2)
Later, someone comes along and adds
public string Value3 { get; set; }
to the MyObject, but forgets to add a
#Html.TextboxFor(model => model.Value3)
This results in Value3 being submitted as null when the form is submitted. What I'm trying to figure out is if it is possible to add something to the form similar to the following:
foreach (var nonExplicitlyUsedProperty in Model.Properties)
{
#Html.HiddenFor(model => model.nonExplicitlyUsedProperty)
}
In the above scenario, this would be the equivalent of adding
#Html.HiddenFor(model => model.Value3)
at the bottom of the form.
You have a couple of options:
Client-Side Validation
Server-Side Validation
Server-Side Sanitize
It sounds like your utilizing a Form Action which will automatically submit data to your Controller. You'll either need to validate the data from the Client before it is passed to the server.
Another approach would be to simply validate the parameter, to avoid a Null being passed or modify your SQL to ignore a Null value passed to it before it does the update.
The other approach would be to couple the functionality of the UI in a manner that is better represented for your Model and Controller. This way it is far more fluid and easy to understand.
Important:
You can use a Hidden Field to store data in when the model is initialized, but your introducing extra data and need to cater / regulate before you can proceed. Though the hack may solve your problem, it isn't ideal truly as it bypasses your problem rather then truly solving it.
#if(Model != null)
{
foreach(var content in Model)
{
<div>
<input type="hidden" id="hdName" name="name" value="#content.Name" />
<div>
}
}
By using this hidden field, you'll then have to ensure when the user does change the field, the hidden field is updated like so:
$('#txtName').blur(function () {
$('#hdName').val($(this).val());
});
As you can see how using Hidden Fields can truly become a nightmare, so I suggest you rethink your solution.
So here is the final answer for what I have been trying to do...
First build out the form (as normal).
Then generate hidden input fields for all of the properties on the model like so... using string format to give it slightly different name/id attributes from the explicitly defined input fields' name/id. (Since ASP.Net passes values from view to controller by name instead of by id, but jQuery standard seems to be to use ids instead of name, account for both situations)
#foreach (var property in Model.GetType().GetProperties())
{
<input type="hidden" name="#(String.Format("{0}_2", property.Name))" id="#(String.Format("{0}_2", property.Name))" value="#property.GetValue(Model, null)" />
}
Afterwards, use jQuery to iterate through the hidden inputs, remove the discriminator that was added to the "name" property to get the actual property name, then check if the input with id of that value (the original property name) has a value. If it DOES have a value, the hidden input is not needed and can be removed, otherwise, rename the hidden input name attribute (since that's how ASP.Net passes the value to the controller) to the original property name.
<script>
$(document).ready(function() {
$("input[type='hidden']").each(function() {
var actualPropertyName = $(this).attr("id").substr(0, ($(this).attr("id").length - 2));
var value = $("#" + actualPropertyName).val();
if (value != null) {
$(this).remove();
} else {
$(this).attr("name", actualPropertyName);
}
});
});
</script>
I have a form like ...
#using (Html.BeginForm("create", "Account", FormMethod.Post, new { id = "accountform_form" }))
{
#Html.TextBoxFor(e => e.ShipFirstName)
...
}
while testing, I was surprised to see the field retained its value on postback even without me assigning it to the view-model. Using the debugger, the value for ShipFirstName is null right at the end of the action when returning the view, so why would it show the value that was in the field? Have I been unnecessarily assigning posted values to view-model properties all this time? Or is there something else going on?
Update: the action is like so...
[HttpPost]
public ViewResult Create(AccountFormModel postModel)
{
var model = new AccountFormModel(postModel, stuff, stuff); //I use posted values and paramters to create the actual view model
return view(model);
}
So, I see the GET form, enter values, say I enter a field and leave a required field blank, submit, the resulting page has the value I entered in the other field, who's putting it there when in the model it's null?
I ran into something similar earlier today (a checkbox was always checked). Have a look at Changing ViewModel properties in POST action and see if this is similar.
Basically calling ModelState.Clear() fixed it for me.
As you're passing the model back to the view after it has been POSTed, MVC is taking the stance that you're doing so because the form contains errors. So, rather than making the user fill out the form again, it repopulates it using the ModelState collection. In this case, the values in the ModelState collection take precedence over the changes you make in the action (which does feel a bit weird).
You can get around this either by calling ModelState.Clear() or using ModelState.Remove(string key), where key is the name of the property.
If you'd like a full explanation of why this is the case, see ASP.NET MVC’s Html Helpers Render the Wrong Value!. Excerpt:
Why?
ASP.NET MVC assumes that if you’re rendering a View in response to an HTTP POST, and you’re using the Html Helpers, then you are most likely to be redisplaying a form that has failed validation. Therefore, the Html Helpers actually check in ModelState for the value to display in a field before they look in the Model. This enables them to redisplay erroneous data that was entered by the user, and a matching error message if needed.
I'm getting the dreaded "The parameters dictionary contains a null entry for parameter 'equipmentid' of non-nullable type" error when attempting to pass data through with a form. I've scoured google and stack overflow for answers and explanations, but I've come to the conclusion the problem has to me be not understanding how this stuff works. Coming from a traditional programming background the web stuff just doesn't seem as intuitive to me.
Here is my view code
#using (Html.BeginForm("CreateReservation", "ReservationList", FormMethod.Get, new { #equipmentID = 1 }))
{
<button class="btn btn-blue">Click</button>
}
Here is my controller
public ActionResult CreateReservation(int equipmentid)
{
// TODO: Query the piece of equipment being reserved.
return View();
}
Can anyone tell me what exactly am I doing wrong? From the answers I've read before and the examples I've viewed, I feel as though this should work.
I am assuming from your code that this is an Asp.Net MVC project, using C# as the language.
There are a number of issues. The first is that the equipmentID variable is not being passed to the view. There are a number of ways to do this. The simplest will be to do it in your controller using the ViewData dictionary, as follows:
public ActionResult CreateReservation(int equipmentid)
{
// TODO: Query the piece of equipment being reserved.
ViewData["equipmentID"] = equipmentid;
return View();
}
Once this is done, you could modify your view code to use the value as follows:
#using (Html.BeginForm("CreateReservation", "ReservationList", FormMethod.Get, new { equipmentID = ViewData["equipmentID"] }))
{
<button class="btn btn-blue">Click</button>
}
This is fine for one or two values, but when the view gets more complex, you rather want to create a ViewModel class, and pass that class to your view.
Secondly - your use of the # in front of equipmentID inside your BeginForm call indicates that you probably need to study up a bit on Razor syntax. A good place to start is Phil Haack's Razor Syntax Quick Reference. Razor syntax is what tells the parser which parts of your view are static HTML that it should output as is, and which parts are C# code that it should execute before sending to the browser. If you are using Visual Studio as your IDE, it is pretty good at highlighting which parts of your code are being interpreted as C# code, and which parts as ordinary HTML.
In my screenshot below you can see that VS2012 highlights C# code with a light grey background colour. The # symbol is the start of C# code - so you don't need to use it again unless you break out of the C# block and go back to HTML.
And thirdly, you probably wouldn't want to pass the equipment ID as an HTML attribute inside your form tag. You should rather create a hidden form field inside your form body and set the name to equipmentID, and the value to the variable's value. The way you are currently using it will add an HTML attribute to the form tag, and your generated code will look something like this:
<form action="/ReservationList/CreateReservation" method="get" equipmentID="1">
And this extra attribute will not be retrievable in your code. Rather do something like this in your view:
#using (Html.BeginForm("CreateReservation", "ReservationList", FormMethod.Get, new { name = "myFormName", id = "myFormID" }))
{
#Html.Hidden("equipmentID", ViewData["equipmentID"])
<button class="btn btn-blue">Click</button>
}
I have an input, type text, element which is being validated using MVC3 validation on the client and I’d like to not have the input value sent to the server when the post occurs.
I have two entities: A “File” entity and a “Company” entity which share a 1 to 1 relationship. The file entity has a CompanyId foreign key.
This is why if you look at the name and id attributes they appear as: File.Company.Code or File_Company_Code.
The reason I want to avoid sending the input value back to the server is when the request reaches the server I only want to bind the values to my entity of type “File”. As it is also receiving a value for “File.Company.Code” it is also attemting to bind the values to the File’s company object, which is what I want to avoid.
The input element is :
<input name="File.Company.Code" id="File_Company_Code" type="text" data-val-required="Se requiere un código de cliente." data-val="true" value=""/>
And the span element:
<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for=" File.Company.Code "/>
I’ve tried:
-Changing the input name and span data-valmsg-for attributes using jquery. But I think that after doing this I may need to rebind the validators??
Any suggestions? (I hope to have explained myself clearly if not let me know.)
Thanks
UPDATE 1 **
Thanks to AFinkelstein sugestion which put me on the right track I updated my domain model such as:
public class FileModel {
public File File {
get {
return this.file;
}
}
*** ADDED this which helped me solve the problem ***
public Company Company {
get {
return this.file.Company;
}
}
}
In my view instead of doing :
#Html.TextboxFor(model => model.File.Company.Code)
#Html.ValidationMessageFor(model => model.File.Company.Code)
I now do:
#Html.TextboxFor(model => model.Company.Code)
#Html.ValidationMessageFor(model => model.Company.Code)
This way the generated name and Id attributes have the value: Company.Code and Company_Code, they dont have the preceding "File". When I receive the post on the server and bind the values to the File object:
FileModel fileModel = new FileModel();
try {
TryUpdateModel(fileModel.File, "File");
as it is not receiving a value for "File.Company.Code" it doesnt attempt to initialize the file's "Company" object, which was causing me other problems.
As it is also receiving a value for “File.Company.Code” it is also attemting to bind the values to the File’s company object, which is what I want to avoid.
I presume this means that File is a domain model within your project. I recommend using a view model in your view.
public class FileViewModel
{
//other stuff contained within the File class
[Required]
public string FileCompanyCode { get; set: }
}
You can use your view model to create or refetch your actual File after posting. Just don't set your actual file company object to the file company code property in the view model. This way it doesn't actually matter if your file company code is binded or not.
I had a similar issue where I wanted the client-side validation but not the field being posted as it was a list of objects and the posting structure didn't support a normal validator.
Still, I was inspired by this question and answer and found out a solution where you add another input field (with all the HTML5 tags that an HTML.HiddenFor would have generated to enable unobtrusive validation) and Html.Validator for the non-existing model property hence the MVC binder in the postback would ignore it.
I also added an Html.ValidatorFor for the real property so that the validation on postback would have somewhere to render as my other validation tags point to a different tag (theoritically)
I have a form that is going through some validation before sending an e-mail.
I have tried using this for validation, where the method ValidateInput sets the ModelState depending on the input:
[HttpPost]
public ActionResult Create(FormCollection collection)
{
ValidateInput(collection);
if (ModelState.IsValid == false) return View(collection);
This clears all the entered fields if something is invalid. I want to keep all the entered data in the field. How can I do that?
Are you using Html helpers for input field in your form or regular html tags?
<%: Html.TextBoxFor(x => x.Message) %> - original value will be kept
<input type="text" name="Message" /> - original value will be lost after postback
Also you could benefit from using build-in validation (with data annotation attributes) instead of using your own validation method.
AFAIK you wouldn't need to call ValidateInput yourself in the Create method - the framework has already set ModelState for you, so just remove the first line.
If you can't change the ValidateInput method to not wipe your properties in this scenario then you'll really need two copies of the form data, one to pass to this method and one in reserve to pass to the view if validation fails.
You may be better off using a specific view model for the actions view rather than relying on the FormCollection object. That way you can use model binding to retrieve the data.
I think the code below should work but I haven't had a chance to test it so beware!
[HttpPost]
public ActionResult Create(MyCreateViewModel collection)
{
MyCreateViewModel myCVM = new MyCreateViewModel();
TryUpdateModel(myCVM);
ValidateInput(myCVM);
if (ModelState.IsValid == false) return View(collection);