fluent validation validating a list of generated text boxes - asp.net-mvc

I have set of textboxes on my form which are generated in a foeach like so:
View:
#for (int i = 0; i < Model.TransomeList.Count; i++)
{
ItemDrops tranItem = Model.TransomeList.ElementAt(i);
<div class="form-group">
#Html.Label(tranItem.ItemName.ToString(), new { #class = "col-sm-6 control-label" })
<div class="col-sm-6">
#Html.TextBoxFor(x => x.TransomeList[i].ItemPossInfo, new { #class = "form-control" })
#Html.HiddenFor(x => x.TransomeList[i].ItemName)
</div>
</div>
}
I'm using fluent validation and want to make sure each text box is required (ideally stating which text box too in the error message)
In my Validator class I have:
RuleFor(x => x.TransomeList).SetCollectionValidator(new TransDropValidator());
with:
public class TransDropValidator : AbstractValidator<ItemDrops>
{
public TransDropValidator()
{
RuleFor(x => x.ItemPossInfo)
.NotNull().WithMessage("Transom position required{O}", x => x.ItemPossInfo);
}
}
However this is not validating anything...what do i need to do?
Thanks

You also need the
#Html.ValidationMessageFor()
I assume you are doing server side validation. If not then futher work is need on your validator and you need to generate the JavaScript component.

Related

ASP.NET MVC - How to Specify the Value in TextBox

How do I specify the Value that should be entered into the Textbox. I dont want to use dropdownlist. I want to accept only these three (3) values Nigeria, Ghana and Togo
If any other value is entered it should not save or hide or disable the Save button.
View
<div class="col-md-12">
<div>
#Html.LabelFor(model => model.COUNTRY_NAME, "Country Name")
#Html.TextBoxFor(model => model.COUNTRY_NAME, new { #style = "border-radius:3px;", #type = "text", #class = "form-control", #placeholder = "Country Name", #autocomplete = "on" })
#Html.ValidationMessageFor(model => model.COUNTRY_NAME, null, new { #class = "text-danger" })
</div>
</div>
Please how do I achieve this?
There are two Options
You can use Server side Custom Validator for Country_Name property.
Or Jquery validation on client side.
When you submit the form, you can validate textbox value by jQuery.
<button onclick="validateData();"></button>
Then write a validation method as follows.
function validateData() {
if (Nigeria, Ghana and Togo) {
//ajax post to your controller
} else {
//return error message
}
}

How to apply bootstrap v4 alpha's form input validation classes with ASP.NET Razor syntax?

So, bootstrap v4 alpha has changed form validation classes a bit. Now, to apply validation styles to a form input, you apply the CSS class to the parent div.form-group.
I'm writing a website using ASP.NET MVC4, and am trying to figure out how to apply this CSS class to a parent HTML element.
For example, here's my current HTML for a form input element ...
<div class="form-group">
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Password)
</div>
If my view's model has a validation error for the Password field, it'll get a corresponding bit of text below the input field. That's what that ValidationMessageFor call does.
But, with bootstrap v4, I need to apply a has-danger class to the parent div.form-group. It'd need to look like so ...
<div class="form-group has-danger">
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Password)
</div>
But, I only want to apply that if there is a validation message for the password field.
Any idea how to achieve this with Razor?
You can create an HtmlHelper that checks ModelState and returns an error class:
public static class HtmlHelperExtensions
{
public static string FieldHasError(this HtmlHelper helper, string propertyName, string errorClass = "has-danger")
{
if (helper.ViewData.ModelState != null && !helper.ViewData.ModelState.IsValidField(propertyName))
{
return errorClass;
}
return string.Empty;
}
public static string FieldHasError<TModel, TEnum>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TEnum>> expression, string errorClass = "has-danger")
{
var expressionString = ExpressionHelper.GetExpressionText(expression);
var modelName = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expressionString);
return FieldHasError(helper, modelName, errorClass);
}
}
Simple usage:
<div class="form-group #Html.FieldHasError("Password")">
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Password)
</div>
or
<div class="form-group #Html.FieldHasError(m => m.Password)">
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Password)
</div>

MVC html helpers change hiddenfor attributes

I have am using html helper fields below, my issue I need the make these hiddenfor elements not hidden when checkbox is checked.
#Html.HorizontalFormFieldFor(model => model.InsaatHizmetBedeli)
<div class="control-group">
#Html.LabelFor(model => model.tadilatMi, new { #class = "control-label" })
<div class="controls">
#if (!Model.tadilatMi.HasValue)
{
Model.tadilatMi = false;
}
#Html.CheckBoxFor(model => model.tadilatMi.Value, new { #Name="tadilatmi" });
</div>
</div>
#Html.HiddenFor(model => model.myHiddenProperty)
here is my jquery code:
$("input[name='tadilatmi']").on("change", function () {
if ($("input[name='tadilatmi']").is(":checked")) {
$("#myHiddenProperty").show()
}
})
of course it not works.. how can I achieve this ?
Your generating an input with type="hidden" which is always hidden. The jQuery.show() method is for toggling the display of elements styled with display:none; and its changes it to display:block;
You could do this by changing the type attribute
if ($("input[name='tadilatmi']").is(":checked")) {
$("#myHiddenProperty").attr('type', 'text')
}
or by making the input type="text" and styling it as hidden
#Html.TextBoxFor(model => model.myHiddenProperty)
with the following css
#myHiddenProperty {
display: none;
}
and then your original script will work.
I suspect however that you want to toggle the visibility back if the checkbox is then unchecked, in which case you should have an else block
if ($("input[name='tadilatmi']").is(":checked")) {
$("#myHiddenProperty").show()
} else {
$("#myHiddenProperty").hide()
}
Side note: your using an awful hack in order to make you checkbox bind to a nullable bool property (by chaninging the name attribute) and your label does not even work as a label (clicking on it will not toggle the checkbox). I recommend you use a view model with
public bool Tadilatmi { get; set; }
and in the view simply use
#Html.LabelFor(m => m.Tadilatmi , new { #class = "control-label" })
<div class="controls">
#Html.CheckBoxFor(m => m.Tadilatmi);
</div>
and change the script to (which is more efficient)
var hiddenElement = $('#myHiddenProperty');
$('#tadilatmi').change(function () {
if ($(this).is(":checked")) {
hiddenElement.show()
} else {
hiddenElement.hide()
}
})
Your myHiddenProperty property could then include a foolproof [RequiredIfTrue("Tadilatmi")] or similar conditional validation attribute.

MVC Bootstrap TextBoxFor EditorFor

I have an MVC view that is using Bootstrap styles. I want to use "#Html.EditorFor" rather than "#HtmlTextBoxFor". Why doesn't EditorFor work out that it needs to be a textbox and then end up with the same result and TextBoxFor??
My reason for asking is that my next field will be a date and want to use EditorFor and with the DataType data annotation it will display a date picker.
Below is screenshot and view code and the Forename is EditorFor whilst the Surname is (the preferred format) TextBoxFor.
<div class="form-group">
#Html.LabelFor(m => m.Title, new { #class = "control-label" })
#Html.DropDownListFor(m => m.Title, new SelectList((IEnumerable)#ViewData["Titles"]), new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Forenames, new { #class = "control-label" })
#Html.ValidationMessageFor(m => m.Forenames)
#Html.EditorFor(m => m.Forenames, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Surname, new { #class = "control-label" })
#Html.ValidationMessageFor(m => m.Surname)
#Html.TextBoxFor(m => m.Surname, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.DateOfBirth, new { #class = "control-label" })
#Html.ValidationMessageFor(m => m.DateOfBirth)
#Html.EditorFor(m => m.DateOfBirth, new { #class = "form-control" })
</div>
I am posting this answer as it worked perfectly for me - all thanks to #StephenMuecke. Although tagged as MVC4 (I will remove the tag) I was actually using MVC5 and therefore could pass html attributes:
<div class="form-group">
#Html.LabelFor(m => m.Forenames, new { #class = "control-label" })
#Html.ValidationMessageFor(m => m.Forenames)
#Html.EditorFor(m => m.Forenames, new { #class = "form-control" })
</div>
becomes:
<div class="form-group">
#Html.LabelFor(m => m.Forenames, new { #class = "control-label" })
#Html.ValidationMessageFor(m => m.Forenames)
#Html.EditorFor(m => m.Forenames, new { htmlAttributes = new { #class = "form-control" } })
</div>
TextBoxFor accepts an attributes parameter because it always creates <input> tags, and thus, it can add attributes to them.
However, EditorFor can render anything from a single <input> tag, to a fully fledged editor (created by declaring a custom editor, or by passing a complex type to the editor). So, it makes no sense to accept an attributes parameter for this case. If you look at the overloads list for this method in MSDN you'll see that, if you pass an object, that object is treated as "additional ViewData", but never as "attributes". If you look at TextBoxFor docs, you'll see that there are several overloads that accept an attributes parameter.
However, the latest versions of MVC (5.1+) do accept attributes in EditorFor helper. Please, see this SO Q&A: Does Html.EditorFor support more than class in MVC5.1?.
Html.EditorFor(Function(m) m.Forenames, New With {
Key .htmlAttributes = New With { Key .[class] = "form-control" }
})
FYI - according to the Telerik code converter, this is what the VB.NET razor version of the answer looks like (yuck). But, maybe useful for someone else out there.
(tested and works)
I use the following workaround to solve the problem for the moment (until I upgrade to mvc5).
Add this to the end of your _layout.cshtml file:
<script type="text/javascript">
var inputs = $('input, textarea, select')
.not(':input[type=button], :input[type=submit], :input[type=reset]');
inputs.addClass('form-control');
</script>

Send single value through RouteValueDictionary

One of the properties of my ViewModel is an array which, unfortunately, is null every time I post back to the controller. I figured a simple hack where I place the values into a coma-delimited string.
This works great for our paging plugin, which posts back to our Index method, using a RouteValueDictionary. However, it is not working in the Html.BeginForm helper which posts back to a different controller action (the Update method).
View
#*Since we can't send arrays or complex objects break array down into string for RouteValueDictionary*#
var channelCodes = "";
for (int i = 0; i < Model.searchChannelCode.Length; i++)
{
channelCodes += Model.searchChannelCode[i];
if (i + 1 < Model.searchChannelCode.Length)
{
channelCodes += ",";
}
}
#*The 'searchChannelCodesPagin' variable from this RouteValueDictionary always posts back as null
using (Html.BeginForm("Update", "ZipCodeTerritory", new RouteValueDictionary()
{
{"searchChannelCodesPaging", channelCodes }
}, FormMethod.Post, new {id = "UpdateForm"}))
{
#Html.HiddenFor(model => model.searchZip)
#Html.HiddenFor(model => model.searchTerritory)
#Html.HiddenFor(model => model.searchState)
#Html.HiddenFor(model => model.searchActiveOnly)
#Html.HiddenFor(model => model.zipCodeTerritory)
<div id="cloneBox">
<div id="rw1">
#Html.LabelFor(model => model.newTerritory)
#Html.TextBoxFor(model => model.newTerritory, new { style = "width: 30px;padding-left:10px;", maxLength = 3 })
#Html.LabelFor(model => model.newDescription)
#Html.TextBoxFor(model => model.newDescription, new { style = "width: 250px;padding-left:10px;", maxLength = 30 })
#Html.LabelFor(model => model.newEffectiveDate)
#Html.TextBoxFor(model => model.newEffectiveDate, new { style = "width: 80px;padding-left:10px;" })
<div id="rw2" style="padding-top: 10px;">
#Html.LabelFor(model => model.newChannelCode)
#Html.DropDownListFor(model => model.newChannelCode, Model.ChannelCodes, " ")
#Html.LabelFor(model => model.newStateCode)
#Html.DropDownListFor(model => model.newStateCode, Model.StateCodes, " ")
#Html.LabelFor(model => model.newEndDate)
#Html.TextBoxFor(model => model.newEndDate, new { style = "width: 80px;" })
</div>
</div>
</div>
<br/>
<div id="buttonDiv">
<button type="submit" id="CloneButton" name="button" value="clone">Apply New Data</button>
<button type="submit" id="deleteButton" name="button" value="delete">Delete Selected Items</button>
<button type="submit" id="removeButton" name="button" value="removeErrors">Remove Selected Errors</button>
</div>
}
Controller
The forma above posts to this controller action. The searchChannelCodePaging variable is null each time.
[HttpPost]
public ActionResult Update(ZipCodeIndex updateZip, string button, string searchChannelCodesPaging)
{
Since you are doing a post, the simplest way to get it to the backend would be add a hidden field:
#Html.HiddenFor("searchChannelCodesPaging", searchChannelCodesPaging);
As a routing value, you may need to get it explicitly within the control via one of the two following approaches. These objects are directly accessible within the Controller class.
RouteData.Values("searchChannelCodesPaging")
Request.QueryString.Get("searchChannelCodesPaging");
You don't have to serialize a array type model parameter to a CSV string to get it to post to your controller. You can do this instead:
#for (var i = 0; i < Model.searchChannelCode.Length; i++)
{
#Html.HiddenFor(m => m.searchChannelCode[i]);
}

Resources