control name in TextBoxFor in MVC3 - asp.net-mvc-3

Is it possible to control the name attribute of say a text boxt when using TextBoxFor?
this code in the view
#Html.TextBoxFor(m => m.SearchParams.someParam)
produce this
<input id="SearchParams_someParam" name="SearchParams.someParam" type="text" value="">
but I do not want my input name to be "SearchParams.someParam" and I wanted that to be something like
<input id="SearchParams_someParam" name="MyPreferedName" type="text" value="">
where the MyPreferedName comes from from some attributes the .SearchParams.someParam in the corresponding model.
Is this possible? I know #Html.TextBox does it but I do not want to hardcode the name in the view.

#Html.TextBoxFor(model => model.attr, new { Name = "txt1" })
Just Use "Name" instead of "name"

It seems as though the TextBoxFor method will not allow you to use the #name key as an htmlAttribute. This makes a little bit of sense because if it did allow you to override the HTML name attribute, the value would not get binded to your model properly in a form POST.
Instead of using...
#Html.TextBoxFor(x => Model.MyProperty, new { #name = "desired_name" }); // does not work
I ended up having to use...
#Html.TextBox("desired_name", Model.MyProperty); // works
I am not exactly sure why the first option does not work but hopefully this helps get around it.

Use TextBox instead of TextBoxFor
#Html.TextBox("MyPreferedName", Model.SearchParams.someParam)

TextBoxFor doesn't allow the name attribute to be set. This workaround:
#Html.EditorFor(m => m.UserName, null, "user_name")
outputs:
<input class="text-box single-line" id="user_name" name="user_name" type="text" value="" />

Don't this work?
#Html.TextBoxFor(m => m.SearchParams.someParam, new { id="my_id" }) although for your specific case it's be more like:
#Html.TextBoxFor(m => m.SearchParams.someParam, new { name = "MyPreferedName" })
Here you'll find the different overloads for the constructor of the InputExtensions.TextBoxFor in MVC3

Related

Custom Editor template based on ViewModel Dataannotation attribute MVC4

What I want to do is automatically add an image span after my input textboxes if the [Required] attribute decorates my ViewModel property be it an integer, double, string, date etc
For example, my ViewModel might look like
public class MyViewModel
{
[Required]
public string Name { get; set; }
}
and my View would look like
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
and the output would be something like
<input id="Name" class="text-box single-line" type="text" value="" name="Name" data-val-required="The Name field is required." data-val-length-max="20" data-val-length="The field Name must be a string with a maximum length of 20." data-val="true">
<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for="Name"></span>
-- Note the automatically added span
<span class="indicator required" style="width: 11px;"></span>
I was intending to have some css that would show the image i.e.
span.required {
background-image: url("required.png");
}
Is this possible to do or do I need to create my own Helper method to implement this type of functionality?
Yes, it's possible, but in general I wouldn't recommend it, because templates are really there to customize type rendering, and you should be able to create templates without worrying if it overrides another template.
I would instead create a custom LabelFor helper, such as the one described here:
http://weblogs.asp.net/imranbaloch/archive/2010/07/03/asp-net-mvc-labelfor-helper-with-htmlattributes.aspx
or here:
http://weblogs.asp.net/raduenuca/archive/2011/02/17/asp-net-mvc-display-visual-hints-for-the-required-fields-in-your-model.aspx
A third option is to not do anything in MVC, but rather add some javascript that will add the indicator based on the standard MVC validation data attributes (if you're using unobtrusive validation). See the answer here:
https://stackoverflow.com/a/8524547/61164
What I did was to modify the jquery.validate.unobtrusive JS file to add a second container, specifically for your images, if there is a validation error.
var container2 = $(this).find("[data-valimg-for='" + escapeAttributeValue(inputElement[0].name) + "']"),
replace = $.parseJSON(container.attr("data-valimg-replace")) !== false;
container2.removeClass("img-validation-valid").addClass("img-validation-error");
Then don't forget to bind it to the model:
error.data("unobtrusiveContainer", container2);
Finally, empty it in the if (replace) code block:
if (replace) {
container.empty();
container2.empty();
error.removeClass("input-validation-error").appendTo(container);
}
else {
error.hide();
}
On success, remember to hide it:
var container2 = error.data("unobtrusiveContainer"),
replace = $.parseJSON(container.attr("data-valimg-replace"));
if (container2) {
container2.addClass("img-validation-valid").removeClass("img-validation-error");
error.removeData("unobtrusiveContainer");
if (replace) {
container2.empty();
}
}
If you take a look at the onError and onSuccess functions in the file, you should be able to find out where you can put them in.
In your view, add the following line of code to each form input there's validation for:
<img class="img-validation-valid" data-valimg-replace="true" data-valimg-for="<replace with field name here, ie. Name>" src="required.png" />
I've only tested this with the [Required] attribute, but it works. I'm also pretty sure you can use this for generating other stuff as well, not just images.

MVC3 - Pass back a model from RenderPartial

I have a page in MVC3 with a model of "pageModel".
In this page I have:
#{ Html.RenderPartial("_subPage", Model.subModel); } (Pagemodel.submodel)
In my controller I am doing:
[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Results(pagemodel model, string frmAction)
{
}
The page loads fine the first time, but when I postback into the httpPost action, model.submodel is always null.
My question is, how do I return an updated model from the RenderPartial (if at all). I can get my model INTO the partial, but not back!
The problem with partials is that they do not preserve the navigational context. This means that any input fields that you might have put inside this partial will have incorrect names and the default model binder will not be able to retrieve the values back when you POST. Your HTML will look like this:
<input type="text" name="Prop1" value="property 1 value" />
<input type="text" name="Prop2" value="property 2 value" />
whereas the correct is:
<input type="text" name="subModel.Prop1" value="property 1 value" />
<input type="text" name="subModel.Prop2" value="property 2 value" />
In order to achieve this correct markup I would recommend you using editor templates.
So you replace:
#{ Html.RenderPartial("_subPage", Model.subModel); }
with:
#Html.EditorFor(x => x.subModel)
and then you move your _subPage.cshtml partial into ~/Views/Shared/EditorTemplates/SubModelType.cshtml where SubModelType is the type of the subModel property:
#model SubModelType
#Html.EditorFor(x => x.Prop1)
#Html.EditorFor(x => x.Prop2)
Now when you look at the generated HTML the corresponding input field names should be prefixed with subModel and inside the POST controller action the model.subModel property will this time be properly initialized and populated from the values that were entered by the user in the input fields.
you'll need to change your partialview to accept the top level model, i.e:
#{ Html.RenderPartial("_subPage", Model); }
which would then render your properties in the partialview with the correct property names i.e. :
<input type="text" name="subModel.MyProperty" value="somevalue" />
It would also mean that your returned model in the HttpPost action will have to correct navigational relationship intact.
this is just one of those caveats related to viewmodels and hierarchies. Oh, btw, in mvc3, you don't need the verbose [AcceptVerbs(HttpVerbs.Post)] for posts. You can simply use [HttpPost]
You can also perform the following.
#Html.RenderPartial(
"_subPage",
Model.subModel,
new ViewDataDictionary
{
TemplateInfo = new TemplateInfo
{
HtmlFieldPrefix = "subModel"
}
});
Your partial view will remain as is, using the #model SubModel

How to get model's field name in custom editor template

I'm building my first custom editor template for a text area control. My code so far is -
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<%= Html.TextAreaFor( Model => Model , 2, 30,
new { #class = "html", #placeholder = ViewData.ModelMetadata.Watermark }) %>
It's not much so far, but it does work. But we need to add a character counter field to show remaining number of characters that the user can type in. I know how to do all the JavaScript to make this work.
So to keep naming system same, I'm going to add a control named ".charCounter" to display number of remaining characters left. My problem is that I cannot figure out the correct syntax to be able to retrieve the field name for the model.
The final version will look something like (JavaScript omitted) -
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<%= Html.TextAreaFor( Model => Model , 2, 30,
new { #class = "html", #placeholder = ViewData.ModelMetadata.Watermark }) %>
<span class="xxx">Remaining characters -
<input readonly type="text" name="<fieldName>.charCounter" />
</span>
You could use ViewData.TemplateInfo.HtmlFieldPrefix, like this:
<input
readonly="readonly"
type="text"
name="<%= ViewData.TemplateInfo.HtmlFieldPrefix %>.charCounter"
/>
I ran into an issue where the PropertyName wasn't returning the Model prefix eg Contact.FirstName. I was able to have it return the HTML field Id using this:
#ViewData.TemplateInfo.GetFullHtmlFieldId("")
Returns:
Contact_FirstName
Respectively you can return the field name using:
#ViewData.TemplateInfo.GetFullHtmlFieldName("")
Returns:
Contact.FirstName
ViewData.ModelMetadata.PropertyName works nicely in MVC3.
In razor:
<input readonly="readonly" type="text" name="#ViewData.ModelMetadata.PropertyName">
Getting just the model name/Id (e.g BirthDate):
ViewData.ModelMetadata.PropertyName;
Getting the model name with prefixes for complex objects (e.g Identity.Person.BirthDate):
#Html.NameFor(m => Model);
or
ViewData.TemplateInfo.HtmlFieldPrefix;
Getting the model Id with prefixes for complex objects (e.g Identity_Person_BirthDate):
#Html.IdFor(m => Model)
or
#Html.IdForModel()

ASP.Net MVC3 Html.PasswordFor does not populate

In my view I have the following element
#Html.PasswordFor(model => model.Password)
This is on a screen that creates/updates user details. When I am trying to update the user this field remains blank. When I change this element to a TextBoxFor it gets the data. How do I get to populate the Password field.
As described above, it is better to avoid doing this for security reason. if you still want to persist the password so that you proceed from where the current validation failed, you can use the HTML helper with html attribute parameter:
Html.PasswordFor(x => x.Password, new { value = Model.Password})
This is as designed. Passwords are not filled to prevent accidental resubmits, and to prevent the page from containing unencrypted passwords. Obviously the password was wrong to begin with if you're posting back the credentials.
In your case, you could create an extension that does input the data, or just use an HTML input of type password.
MVC protects you from doing something like this for a reason. You shouldn't actually be able to do this because the users password should not be stored unencrypted and unhashed. If your goal is to end end up on http://plaintextoffenders.com/ though, you can do something like:
<input type="password" name="Password" id="Password" value="#Model.Password" />
I found this workaound. I needed my password shown in the form:
#model User
#{
#Html.Label(Model.Username, new { #class = "label" })
#Html.TextBoxFor(Model => Model.Username, new { #class = "form-control" })
#Html.Label(Model.Password, new { #class = "label" })
#Html.TextBoxFor(Model => Model.Password, new { #class = "form-control make-pass" })
}
<script type="text/javascript">
$(".make-pass").attr("type", "password");
</script>
This will make your input password-type without losing the value.
As mentioned before, its by design. But that can be wrong. We just got a new user who was confused by the empty password after the page refresh when he just added a password. So a simple fix is to change the label 'Password' to 'New password'. Now its clear why this input box is always empty.
If you're using asp-for attribute you can also do:
<input type="password" placeholder="#(Model.Password != null ? String.Concat(Model.Password.ToCharArray().Select(p=>"*")) : "Password")" asp-for="Password">

ASP.NET MVC 3 - Validation Question

Good evening everyone I have a question regarding validation of drop-down list values. I have a view that is bound to a view model type called ReservationData.
This object contains a property CustomerVehicles of type List<VehicleData>. VehicleData has two int properties VehicleMakeId and VehicleModelId.
On my view I am trying to loop over the number of items in the CustomerVehicles collection and displaying two dropdowns for each, a vehicle make dropdown and a vehicle model dropdown using DropDownListFor.
When I try to submit and validate I do not see any validation errors displayed on the screen.
Just in case you are wondering I have added a ValidationMessageFor for each dropdown as well. I am not sure if this is an issue with the structure of my view model and its complexity and how the controls need to be named or how the ids need to be set. Any help would be greatly appreciated.
Here is the code for the looping over the collection:
#for (var i = 0; i < Model.CustomerVehicles.Count(); i++)
{
var vehicleNumber = i + 1;
<div class="vehicle-selection-wrapper">
<div class="content-container">
<h3>
Vehicle #vehicleNumber</h3>
<img class="vehicle-image" alt="manufacturer image" src="#Url.Content("~/Content/images/default-vehicle.gif")" /><br />
#Html.LabelFor(m => m.CustomerVehicles[i].VehicleMakeId)
#Html.DropDownListFor(m => m.CustomerVehicles[i].VehicleMakeId
, new SelectList(Model.VehicleMakes, "Id", "Name")
, #UIDisplay.Dropdown_DefaultOption, new { #class = "long-field" })<br />
#Html.ValidationMessageFor(m => m.CustomerVehicles[i].VehicleMakeId)<br />
#Html.LabelFor(m => m.CustomerVehicles[i].VehicleModelId)
#Html.DropDownListFor(m => m.CustomerVehicles[i].VehicleModelId
, new SelectList(new List<CWR.Domain.VehicleModel>(), "Id", "Name")
, #UIDisplay.Dropdown_DefaultOption, new { #class = "long-field" })
#Html.ValidationMessageFor(m => m.CustomerVehicles[i].VehicleModelId)
</div>
</div>
}
Ok so I also noticed that in the generated HTML the selects that are generated are missing the HTML5 data-val attributes that are associated to elements to handle validation. Here is the generated HTML
<select class="long-field" id="CustomerVehicles_0__VehicleMakeId" name="CustomerVehicles[0].VehicleMakeId"><option value="">-- Select --</option>
</select><br />
<span class="field-validation-valid" data-valmsg- for="CustomerVehicles[0].VehicleMakeId" data-valmsg-replace="true"></span><br />
<label for="CustomerVehicles_0__VehicleModelId">Model</label>
<select class="long-field" id="CustomerVehicles_0__VehicleModelId" name="CustomerVehicles[0].VehicleModelId"><option value="">-- Select --</option>
</select>
<span class="field-validation-valid" data-valmsg-for="CustomerVehicles[0].VehicleModelId" data-valmsg-replace="true"></span>
Additionally in my VehicleData class the VehicleMakeId and VehicleModelId properties are decorated with a Required attribute.
UPDATE:
Ok so I was testing and noticed that if I keep my code identical except I swap the Html.DropdownListFor calls with Html.TextboxFor calls then the validation works. What could be causing this? Could it be a framework bug with the unobtrusive validation?
UPDATE: Contains Fix
So after posting this same question on the ASP.NET Forums, I was able to get a solution. In the post you will be able to see that there is a bug in the unobtrusive validation framework and how it handles validation of dropdownlists. The user counsellorben does a good job in explaining the problem as well as a solution (including sample code) that will assist others in avoiding this issue in the future, or at least until Microsoft builds in a fix in to the framework.
Thank you everyone for your assistance.
I too have come across this obviously massive oversight regarding client side validation with dropdownlists in MVC 3 and the best solution I can offer is to put the missing HMTL attributes in yourself.
In your view model create a property like this.
public Dictionary<string, object> CustomerVechicleAttributes
{
get
{
Dictionary<string, object> d = new Dictionary<string, object>();
d.Add("data-val", "true");
d.Add("data-val-required", "Please select a Vechicle.");
return d;
}
}
Then in your code, enter
#Html.DropDownListFor(m => m.CustomerVehicles[i].VehicleMakeId
, new SelectList(Model.VehicleMakes, "Id", "Name")
, #UIDisplay.Dropdown_DefaultOption,
**Model.CustomerVechicleAttributes** })
Just add the Model.CustomerVechicleAttributes as htmlAttributes to your dropdownlist.
This will inject the necessary attributes that are missing. You will of course need to add any other attributes you may need like your class attribute.
Hope this helps.
This is the simpliest way I found to do it, just adding data-val-*-* attributes in HtmlAttributes of DropDownListFor, inside the view. The following method works with RemoteValidation too, if you do not need remote validation, simply remove the elements containing data-val-remote-*:
#Html.DropDownListFor(m => m.yourlistID, (IEnumerable<SelectListItem>)ViewBag.YourListID, String.Empty,
new Dictionary<string, object>() { { "data-val", "true" },
{ "data-val-remote-url", "/Validation/yourremoteval" },
{ "data-val-remote-type", "POST" }, { "data-val-remote-additionalfield", "youradditionalfieldtovalidate" } })
I hope it may help. Best Regards!
you should try to add data annotations on your view model properties first so you could see the validation messages.
you might find what you need here
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.aspx
or create custom ones if needed.
what exactly do you need to validate?
I had exactly the same problem with the field getting correctly validated in TextBoxFor but not in DropDownListFor.
#Html.DropDownListFor(m => m.PaymentTO.CreditCardType, Model.CreditCardTypeList, "Select Card Type", new { style = "width:150px;" })
Since I had another DropDownListFor working on the same page, I knew that it wasn’t a generic DropDownListFor problem. I also have a complex model and parent object PaymentTO wasn’t initialized. When I set viewTO.PaymentTO = new PaymentTO(); in the Controller, the validation for the DropDownListFor started to work. So there is probably a problem with DropDownListFor, but the fix can be as simple as initializing the object in the controller.

Resources