How to get model's field name in custom editor template - asp.net-mvc-3

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()

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 Drop Down or ListBox with dynamic check boxes

I have an MVC3 C#.Net web app. I have a requirement to display a list of check boxes (either in a drop down or a List Box) dynamically based on a table in our db. The table can contain 1:500 entries. I then need to pass the selected check boxes to the Controller in order to perform an action on each selection. Any ideas on an implementation?
There are many ways to go about this but here is a general demo.
You can pass a list of the checkbox values and descriptions either on the Model (as this example does), ViewData[""] dictionary or ViewBag (MVC3+).
Then in your view loop through them and add them to your form using the name="chxBxGroupName" to group them.
Now create a controller action to post to that takes a List of the value type (in this example int) with the parameter naming matching the name="chxBxGroupName".
It will post a list of checked values.
The post page will just print out:
123
456
// This is your landing page. It gathers the data to display to the user as checkboxes.
public ViewResult MyPageWithTheCheckboxes()
{
// For example: Assume a Dictionary with the checkbox value and description
// Replace with DB call, etc.
return View(new Dictionary<int, string> { { 123, "Foo" }, { 456, "Bar" } });
}
<%# Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<Dictionary<int,string>>" %>
<!-- Rest of HTML page... -->
<form id="frm1" action="<%=Url.Action("MyPost")%>" method="post">
<% foreach (var item in Model) { %>
<input type="checkbox" id="chxBx<%=item.Key%>" name="chxBxGroupName" value="<%=item.Key%>"/>
<label for="chxBx<%=item.Key%>"><%=item.Value%></label>
<br/><br/>
<% } %>
<input type="submit" value="Go!"/>
</form>
<!-- Rest of HTML page... -->
public ContentResult MyPost(List<int> chxBxGroupName)
{
return Content(string.Join(Environment.NewLine, chxBxGroupName ?? new List<int>()), "text/plain");
}
the answer provided by Jay accomplishes exactly what you need. If you want to put the items in a list you can do it by surrounding the markup with a div control lie below
<div style="height:40px;overflow:auto;">
<% foreach (var item in Model) { %>
<input type="checkbox" id="chxBx<%=item.Key%>" name="chxBxGroupName" value="<%=item.Key% >"/>
<label for="chxBx<%=item.Key%>"><%=item.Value%></label>
<br/><br/>
<% } %>
</div>
unfortunately you cant have checkboxes in a <select />, but if you really wanted it to look like a select list you could add some JQuery that processes onclick of a textbox and shows the div. This obviously would require some jQuery and might not be worth it

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 add unobtrusive validation for html template fields whats not in the model

i got a template created for custom zip code field.
My template code is below:
#{
string model = Model ?? string.Empty;
string zipFirst = "";
string zipSecond = "";
if(!String.IsNullOrEmpty(model) && !String.IsNullOrWhiteSpace(model))
{
var values = model.Split('-');
if(values.Count() == 2)
{
zipFirst = values[0] ?? string.Empty;
zipSecond = values[1] ?? string.Empty;
}
}
var pName = ViewData.ModelMetadata.PropertyName;
var zipAreaId = "#" + pName + "zipcodearea";
}
<div id="#(pName)zipcodearea">
<input type="text" maxlength="5" id="zipcodefirst" name="#(pName)codefirst" value="#(zipFirst)" style="width:136px"/> -
<input type="text" maxlength="4" id="zipcodesecond" name="#(pName)codesecond" value="#(zipSecond)" style="width:120px"/>
#Html.HiddenFor(m => m, new { #class = "generatedZipField"})
</div>
<script type="text/javascript">
jQuery(function ($) {
$('#(zipAreaId) #zipcodefirst').autotab({ target: '#(pName)codesecond', format: 'numeric' });
$('#(zipAreaId) #zipcodesecond').autotab({ previous: '#(pName)codefirst', format: 'numeric' });
$('#(zipAreaId)').zipcode();
});
</script>
And i use it like this:
[UIHint("ZipCode")]
[Display(Name = "Zip Code")]
public string Zip { get; set; }
Like you see in my template i got two fields whats not included in the model.
It is #zipcodefirst and #zipcodesecond.
What i need to achieve is to have two separate fields for full us zip code.
When user fill both fields im using jquery widget for merging them into one string and inserting it into hidden field in template. after form submited value in hidden field getting sent into server.
Whats the problem?
I need to add mvc unobtrusive validation for them two fields whats not in the model #zipcodefirst and #zipcodesecond.
validation rules
zipcodefirst field must be filled in first
then zipcodefirst field is filled you can fill second field
second field must have 4 digits in it
first field must have five digits
cant fill second field while first one is empty or incorectly filled
Im strugling with validation part for quite a while now.... :(
How i could achieve that thru mvc3 unobtrusive validation tools?
any help will be highly apreciated guys.
Add unobrusive data data validation on the textbox by adding data-val="true" and use a regular expression for your zip code.
<input type="text" data-val="true" data-val-regex="Invalid zip code format" data-val-regex-pattern="YOUR REGEXP HERE" />
UPDATE
If you also want it to be required you can add the data-val-required attribute.
<input type="text" data-val="true" data-val-requred="Zip code is required" data-val-regex="Invalid zip code format" data-val-regex-pattern="YOUR REGEXP HERE" />
More information about validation in MVC 3:
http://bradwilson.typepad.com/blog/2010/10/mvc3-unobtrusive-validation.html

control name in TextBoxFor in MVC3

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

Resources