I'm using #Html.Password("Password", ViewData["password"]) for displaying password in popup, the password is being displayed and I should be able to edit and update it. After editing it, when I click save button password is not being updated.
ModelState.IsValid is giving false and the method is being skipped.
From Password Method signature
public static string Password(
this HtmlHelper htmlHelper,
string name
)
second argument is
The name of the form field and the ViewDataDictionary key that is used to look up the value.
So it is practically wrong to pass ViewData["password"] as second argument.
MVC will automatically look for value into ViewData, so you could just write
#Html.Password("Password")
Or do everything correct way and use strongly typed view models
Related
I have an add product form which depends upon the user having added categories, product types etc. previously. Rather than let them complete the form, and then realise that they can't submit it, I want to show an error when the form first loads.
I want to add this error to the modelstate against the appropriate property - so my question is how can I get the modelstate key name for that property?
Code
Model.Step1.PopulateData(_productTypeSvc.GetList(), _websiteSvc.GetMaximumNumberOfProductImages(HttpContext.Request.Url.Host));
Model.Title = "Add A Product - Step 1: The Basics";
if (Model.Step1.ProductTypes.IsNullOrEmpty())
{
//Rather than string.Empty, I want to programmatically get the correct key to add the error to..
//E.g. if it were the view - HtmlHelper.NameFor(m => m.Step1.ProductTypeID)
ModelState.AddModelError(string.Empty, "Please add at least one product type before adding products.");
}
I know I can dummy the HtmlHelper object, but I'd rather avoid this and wondered if there is a better way?
Found an appropriate extension in MVC:
System.Web.Mvc.ExpressionHelper.GetExpressionText(LambdaExpression expression);
MSDN Here.
Thanks anyway guys.
Update:
public static string GetPropertyName<TModel, TValue>(this TModel model, Expression<Func<TModel, TValue>> propertySelector)
{
return ExpressionHelper.GetExpressionText(propertySelector);
}
Then use the extension:
ModelState.AddModelError(Model.GetPropertyName(m => m.Step1.ProductTypeID), "Please add at least one product type before adding products.");
I use pure html since I do not like the #Html class.
How do I get the value from an option in a select input ?
The model binder gives me the text property of an option and not the value, as I would have liked :)
Do I really have to use Request.From[""] to do it ?
Thanks!
EDIT
This will make the text attribute, not the value attribute, appear in the viewmodel
public ActionResult UpdateCard( UpdateCreditCardViewModel form )...
And if I just add another parameter with the input name, then the viewmodel is correcly populated. I don't have to do anything else it seems.
public ActionResult UpdateCard( UpdateCreditCardViewModel form, string Country )...
I guess that's the default behaviour ?
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.
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 am trying to work around the fact that when they wrote asp.net MVC 3 they forgot to include code to add the unobtrusive validation attributes to select lists and their "fix" for this is to include it in MVC 4, which is no bloody use to anyone using MVC 3.
My proposed work around is to use Html.GetUnobtrusiveValidationAttributes() to add them myself, just like any other custom attributes, but i can't work out the correct syntax for calling the method. There are 2 overloads, one takes a string and the other takes a string and a ModelMetaData class. I understand the metadata param, I presume I just pass in ViewData.ModelMetadata but what should the string be? The MSDN documentation says it is "the specified HTML name attribute" which makes no sense to me. The HTML name attribute of what? The select list? Why would it need that and how does that help it know what property on my model i want the validation for? Looking at examples of usage they all seem to pass in the name of the property on my model that i want the validation attributes for, which makes sense. Unfortunately I can't get the method to return anything but an empty collection no matter what i pass in.
My model class is called Event and my property is called EventTypeID. I am using a slightly different viewmodel class as the basis for the view because i need to display a list of Events and also also allow a new event to be entered on the same view so i have a simple viewmodel class as below:
public class EventViewModel
{
public Model.Event NewEvent { get; set; }
public IEnumerable<Model.Event> Events { get; set; }
}
The dropdown list is mapped to the property like: #Html.DropDownListFor(model => model.NewEvent.EventTypeID what do I pass as the string to Html.GetUnobtrusiveValidationAttributes(string) or Html.GetUnobtrusiveValidationAttributes(string, ModelMetadata) to get the attributes for this property. I have tried:
Html.GetUnobtrusiveValidationAttributes("EventTypeID")
Html.GetUnobtrusiveValidationAttributes("EventTypeID",ViewData.ModelMetadata)
Html.GetUnobtrusiveValidationAttributes("NewEvent.EventTypeID")
Html.GetUnobtrusiveValidationAttributes("NewEvent.EventTypeID",ModelMetadata)
They all return an empty collection.
I know that my model is correct because if i change the call from Html.DropDownListFor to Html.TextBoxFor then the validation "just works" without me having to do anything other than add the validation attributes to my model class.
EDIT:
Just tried turning client side validation off, the validation works fine server side for all select lists.
For those still looking for an answer, this works for me:
public static IDictionary<string, object> UnobtrusiveValidationAttributesFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> propertyExpression)
{
var propertyName = html.NameFor(propertyExpression).ToString();
var metadata = ModelMetadata.FromLambdaExpression(propertyExpression, html.ViewData);
var attributes = html.GetUnobtrusiveValidationAttributes(propertyName, metadata);
return attributes;
}
Note that I'm using .Net MVC 4, you don't have the html.NameFor method in MVC 3. However, I believe this can be done in MVC 3 with the following method:
var propertyName = ExpressionHelper.GetExpressionText(propertyExpression);
You can use it inline
Example for select element
<select name="#Html.NameFor(m=> m.MyProperty)"
id="#Html.IdFor(m=> m.MyProperty)"
#Html.Raw(string.Join(" ", Html.GetUnobtrusiveValidationAttributes(Html.NameFor(m => m.MyProperty).ToString()).Select(x => x.Key.ToString() + "=\"" + x.Value + "\"")))
>
Here is a link to an answer I posted, showing an HtmlHelper I wrote to provide unobtrusive validation for dropdownlists: MVC 3 dropdownlist validation not working for complex view model
UPDATE
Are you trying to get the attributes in an HtmlHelper, or in-line in your view?
Assuming you are trying to get the attributes in your view, that is the problem.
First, you need to understand that ModelMetadata does not represent a single object available across your entire model. Rather, it represents the metadata for a particular element, be it your model, or any property within the model. A better descriptive name would be ObjectMetadata, since ModelMetadata is the metadata for a specified object, be it a model, a nested model, or a specific property.
ModelMetadata in the view is only the metadata for the top-level model. You must get the ModelMetadata for the property to which the dropdownlist is bound. If you use a helper, then the helper is passed the correct ModelMetadata as a matter of course. If you use your view, you need to engage in some gymnastics to get the correct ModelMetadata, see for example my answer here: Validating and editing a “Changeable”/optional type in Asp.net MVC 3