FluentValidation Rule dependency validation based on drop-down selection in MVC4 - c#

I need to validate two textboxes based on dropdownselect. It is dependency validation. for this I am using FluentValidation. Key thing is here, values of dropdownlist is language specification. (Russia, spanish).
RuleFor(x => x.HasMaterialPublishedElseWhereText).NotEmpty().WithMessage(i18n_Models_Abstract.RequiredField);
RuleFor(x => x.DtPublishedTimeText).NotEmpty().When(x => x.HasMaterialPublishedElseWhereText == i18n_Models_Abstract.AbstractYes).WithMessage(i18n_Models_Abstract.RequiredField);
RuleFor(x => x.PublishedPlaceText).NotEmpty().When(x => x.HasMaterialPublishedElseWhereText == i18n_Models_Abstract.AbstractYes).WithMessage(i18n_Models_Abstract.RequiredField);
view
<div class="row" style="padding-bottom: 10px">
<div class="col-md-10">
<div class="col-md-4">
#Html.HiddenFor(model => model.HasMaterialPublishedElseWhereOptions)
#{
options = Model.HasMaterialPublishedElseWhereOptions;
optionsList = options.Split(',').ToList();
optionSelect = optionsList.Select(option => new SelectListItem() { Text = option, Value = option }).ToList();
}
#Html.DropDownListFor(model => model.HasMaterialPublishedElseWhereText, optionSelect, i18n_Models_Abstract.SelectOption +"...", new { #class = "input-validation-error form-control" })
#Html.ValidationMessageFor(model => model.HasMaterialPublishedElseWhereText)
</div>
</div>
</div>
<div class="row" style="padding-bottom: 10px">
<div class="col-md-10">
<div class="col-md-4">
#Html.TextBoxFor(model => model.DtPublishedTimeText, new { #class = "form-control", #placeholder = Model.DtPublishedTimeLabel, maxlength = 40 })
#Html.ValidationMessageFor(model => model.DtPublishedTimeText)
</div>
</div>
</div>
<div class="row" style="padding-bottom: 10px">
<div class="col-md-10">
<div class="col-md-4">
#Html.TextBoxFor(model => model.PublishedPlaceText, new { #class = "form-control", #placeholder = Model.PublishedPlaceLabel, maxlength = 40 })
#Html.ValidationMessageFor(model => model.PublishedPlaceText)
</div>
</div>
</div>
HTML
<div class="col-md-4">
<input id="HasMaterialPublishedElseWhereOptions" name="HasMaterialPublishedElseWhereOptions" type="hidden" value="да/yes,нет/no">
<select class="input-validation-error form-control" data-val="true" data-val-required="обязательное поле" id="HasMaterialPublishedElseWhereText" name="HasMaterialPublishedElseWhereText">
<option value="">Выбрать опции...</option>
<option selected="selected" value="да/yes">да/yes</option>
<option value="нет/no">нет/no</option>
</select>
<span class="field-validation-valid" data-valmsg-for="HasMaterialPublishedElseWhereText" data-valmsg-replace="true"></span>
</div>
<div class="col-md-4">
<input class="form-control" id="DtPublishedTimeText" maxlength="40" name="DtPublishedTimeText" placeholder="Если Да, укажите дату публикации/If yes, Date" type="text" value="">
<span class="field-validation-valid" data-valmsg-for="DtPublishedTimeText" data-valmsg-replace="true"></span>
</div>
<div class="col-md-4">
<input class="form-control" id="PublishedPlaceText" maxlength="40" name="PublishedPlaceText" placeholder="" type="text" value="">
<span class="field-validation-valid" data-valmsg-for="PublishedPlaceText" data-valmsg-replace="true"></span>
</div>
All other validation are working, except this dependency one not working? Anything doing wrong?

Dependent validation doesn't generate appropriate html attributes, because dependency value (true or false) would be known only after user input.
Neither jquery.validate framework (on which client-side validation based), nor MVC model metadata infrastructure (which used for simple rules that could be converted to client-side rules), doesn't support more than one attributes set, so, MVC infrastructure makes decision to generate no attributes at all.
Same logic works for RuleSets customization: rules defined under specified ruleset doesn't participate in validation attributes generation.

Related

MVC .NET - create dropdownlist for each item selected in a listbox

I am currently trying to create/remove certain elements based on the selections in a listbox. My listbox is as follows:
<div class="form-group" id="subIngredients">
#Html.LabelFor(model => model.Recipe.SubIngredientIds, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.ListBoxFor(model => model.Recipe.SubIngredientIds, new MultiSelectList(ViewBag.PossibleIngredients, "Value", "Text", ViewBag.SelectedIngredients), new { #class = "form-control multiselect", style = "height:200px" })
#Html.ValidationMessageFor(model => model.Recipe.SubIngredientIds, "", new { #class = "text-danger" })
</div>
</div>
and my html elements (an input field and a dropdownlist) are as follows:
<div class="editor-label">
<label class="control-label" for="Subitem_#Model.IngredientIdNamePairs.ElementAt(i).Key">#Model.IngredientIdNamePairs.ElementAt(i).Value</label>
</div>
<div class="editor-field">
<div style="display:inline-block">
<input class="form-control text-box single-line subq" data-val="true" data-val-number="The field #Model.IngredientIdNamePairs.ElementAt(i).Value must be a number." data-val-range="Please enter a valid decimal" data-val-range-max="1.79769313486232E+308" data-val-range-min="0" data-val-required="The #Model.IngredientIdNamePairs.ElementAt(i).Value field is required." id="RegularSize_SubIngredientRuns_#Model.IngredientIdNamePairs.ElementAt(i).Key" name="RegularSize.SubIngredientRuns[#Model.IngredientIdNamePairs.ElementAt(i).Key]" type="text" value="#(Model.RegularIngredientIdValuePairs != null && Model.RegularIngredientIdValuePairs.ContainsKey(Model.IngredientIdNamePairs.ElementAt(i).Key) ? Model.RegularIngredientIdValuePairs[Model.IngredientIdNamePairs.ElementAt(i).Key] : 0)" />
</div>
<div style="display:inline-block">
#Html.DropDownListFor(m => m.SelectedRecipeUnit, Model.BaseUnitOptions, "Select Unit", new { #class = "form-control dropdownlistReg", #data_key = #Model.IngredientIdNamePairs.ElementAt(i).Key })
</div>
<span class="field-validation-valid text-danger" data-valmsg-for="Subitem_#Model.IngredientIdNamePairs.ElementAt(i).Key" data-valmsg-replace="true"></span>
</div>
And an image to illustrate:
how can I create a new input field and dropdownlist for each item selected in the listbox? All help is appreciated.
You won't be able to do it on the fly with .Net MVC. You'll have to include some javascript.
<script src="http://code.jquery.com/jquery-1.12.3.min.js"></script>
<body>
<select id="bigList" name="sometext" size="5" multiple>
<option>text1</option>
<option>text2</option>
<option>text3</option>
<option>text4</option>
<option>text5</option>
</select>
<select id="ddl">
</select>
</body>
$("#bigList").click(function()
{
var results = $(this).find(':selected');
$("#ddl").empty();
for(var cnt=0; cnt < results.length; cnt++)
{
$("#controlz").append(results[cnt].text + "<input type=textbox></input> </br>");
// $("#ddl").append("<option>" +results[cnt].text + "</option>");
}
});
I put it in a JS fiddle here: https://jsfiddle.net/w0z144s1/9/

TagBuilder not generating unqiue id attribute values across ajax requests

I have an issue where I'm using the same template to render some content on a page and the same template is used to render additional content on the page using an AJAX request.
The following code renders the partial razor view:
#model SearchBoxViewModel
<div class="smart-search">
#using (Html.BeginForm("Index", "Search", FormMethod.Get, new { #class = "form-horizontal", role = "form" }))
{
<div class="form-group">
<div class="hidden-xs col-sm-1 col-md-1 col-lg-1 text-right">
#Html.LabelFor(m => m.SearchPhrase, new { #class = "control-label heading" })
</div>
<div class="col-xs-8 col-md-9 col-lg-10">
#Html.TextBoxFor(m => m.SearchPhrase, new {#class = "form-control", placeholder = "for products, companies, or therapy areas"})
</div>
<div class="col-xs-4 col-sm-3 col-md-2 col-lg-1">
<input type="submit" value="Search" class="btn btn-default"/>
</div>
<div class="what-to-search hidden-11 col-sm-11 col-sm-offset-1">
<div class="checkbox">
<label>
#Html.CheckBoxFor(m => m.WhatToSearch, null, false)
#Html.DisplayNameFor(m => m.WhatToSearch)
</label>
</div>
</div>
</div>
}
</div> <!-- /.smart-search -->
The following code makes the AJAX request:
$.ajax(anchor.attr("data-overlay-url-action"))
.done(function (data) {
$("div.overlay").addClass("old-overlay");
$("div.navbar").after(data);
$("div.overlay:not(.old-overlay)")
.attr("data-overlay-url-action", anchor.attr("data-overlay-url-action"))
.hide()
.fadeIn();
$("div.old-overlay")
.fadeOut()
.removeClass("old-overlay");
anchor.addClass("overlay-exists");
});
So what you get is the same partial razor view output on the page twice, once during the page request and once during the AJAX request.
The problem is that TextBoxFor, CheckBoxFor, etc. all make use of TagBuilder.GenerateId to generate the id attribute value but it doesn't account for generating id's across multiple requests where AJAX might be involved. This results in the same id value being output on the page, causing JavaScript to break.
The following is the HTML that is output twice (once during the request and then added in a separate part of the page during an AJAX request):
<div class="smart-search">
<form role="form" method="get" class="form-horizontal" action="/PharmaDotnet/ux/WebReport/Search"> <div class="form-group">
<div class="hidden-xs col-sm-1 col-md-1 col-lg-1 text-right">
<label for="SearchPhrase" class="control-label heading">Search</label>
</div>
<div class="col-xs-8 col-md-9 col-lg-10">
<input type="text" value="" placeholder="for products, companies, or therapy areas" name="SearchPhrase" id="SearchPhrase" class="form-control">
</div>
<div class="col-xs-4 col-sm-3 col-md-2 col-lg-1">
<input type="submit" class="btn btn-default" value="Search">
</div>
<div class="what-to-search hidden-11 col-sm-11 col-sm-offset-1">
<div class="checkbox">
<label>
<input type="checkbox" value="true" name="WhatToSearch" id="WhatToSearch">
NewsManager Search Only
</label>
</div>
</div>
</div>
</form></div>
So the SearchPhrase and WhatToSearch id's are duplicated.
Is there any way to work around this, or is there a better way to render the form elements to avoid this issue?
You could specify your own ID for the items, and then you wouldn't have this ID collision problem. Have you tried setting the id manually: Html.TextBoxFor( ... , new { id = "AnythingHere" }), where the id could be a freshly generated Guid? (Note that you'd probably need to add a prefix, because Guids can start with a number.
So you can use the following. The Guid doesn't look good, and is unnecessarily long. You might want to go with something shorter, like short guid, DateTime.Ticks, ...
#{
var id = "chk_" + Guid.NewGuid().ToString();
}
#Html.CheckBoxFor(..., new { id = id })
#Html.LabelFor(..., new { #for = id })

ASP .NET MVC How to increase the width of an inputfield with #Html.EditorFor

I just cant fix something that seems so easy.
I want a textbox (editorfor) for a model property and I would like to increase its width but nothing is happening. I'm using the code as listed below. I tried setting the width to 500px but nothing happens. Ideally I would like the textbox to stretch over the full width of the container. Any ideas?
#if (Model.isAnswerVisible)
{
<div class="form-group">
<div class="col-md-10">
<div class="input-group">
<span class="input-group-addon">#Model.AreaNameAnswer</span>
#Html.EditorFor(model => model.Answer, new { htmlAttributes = new { id = "answer", style = "width: 500px", onkeyup = "limitCharacters()", #class = "form-control" } })
</div>
#Html.ValidationMessageFor(model => model.Answer, "", new { #class = "text-danger" })
<span class="glyphicon glyphicon-question-sign" aria-hidden="true" title="Leg kort uit wat jouw antwoord is op de vraag"></span>
<label id="lblCountAnswer" style="color: green">#Model.MaxTokensAnswer</label>
</div>
</div>
}
The default MVC5 template has a css rule in Site.css:
input, select, textarea { max-width: 280px; }
I usually remove this rule.
It causes problems with Bootstrap v3 input group where the input box does not extend to its container's width.
Reverse the order of your elements and you get a gap:
Instead of this
<div class="row">
<div class="col-md-4">
<div class="input-group">
<input type="text" class="form-control" />
<span class="input-group-btn">
<button class="btn btn-primary">OK</button>
</span>
</div>
</div>
</div>

Bootstrap 3 input-group-addon suffix not attached to form field

Using Bootstrap 3 as part of an C# ASP.NET MVC5 project.
I'm getting a very strange issue when trying to use input-group-addon to add a 'GB' suffix to a form field.
Here's the C#:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.capacity, new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="input-group">
#Html.TextBoxFor(model => model.capacity, new { #class = "form-control" })
<span class="input-group-addon">GB</span>
</div>
#Html.ValidationMessageFor(model => model.capacity)
</div>
</div>
</div>
}
Which equates to:
<form action="/NewSpaceRequest/Create" method="post"><input name="__RequestVerificationToken" type="hidden" value="tP5XKKhflSS8K1e7q1I_ZSuR8Ty7X88FfuOMG5JhmF-KXtUOcn4SQdNGTMZZVC2pdBMjb7RgNrIB90Y9I5C_FgX9xmMLrwrbiFZde7PRZ13gCqjBDAVglM38T7j09-C-uNkRSCZbFqDgiU_XcH9D-w2" />
<div class="form-horizontal">
<hr />
<div class="form-group">
<label class="control-label col-md-2" for="capacity">Capacity</label>
<div class="col-md-10">
<div class="input-group">
<input class="form-control" data-val="true" data-val-number="The field Capacity must be a number." data-val-range="The field Capacity must be between 1 and 2147483647." data-val-range-max="2147483647" data-val-range-min="1" data-val-required="The Capacity field is required." id="capacity" name="capacity" type="text" value="" />
<span class="input-group-addon">GB</span>
</div>
<span class="field-validation-valid" data-valmsg-for="capacity" data-valmsg-replace="true"></span>
</div>
</div>
</div>
</form>
Notice how the suffix isn't 'pulled left' to the form field?
Not sure why.
The framework stack (ASP.NET, MVC5) comes with additional styles on top of Bootstrap, found in Site.css, which statically sets the maximum with of input fields.
You can safely remove this entire entry.
Upon doing this, your form fields will be super long. To remedy this, I changed col-md-10 to something shorter like col-md-4, works a treat.

MVC model binding, Initial Model Missing

In this instance I have two pages, in the post method of the first I return the view/viewmodel of the second like so :
[HTTPPost]
public Task<ActionResult> Page1(Page1Model model)
{
var Page2Model = GrabDataMethod(model);
return View("Page2", Page2Model); //Point 1
}
[HTTPPost]
public Task<ActionResult> Page2(Page2Model model //Point 2)
{
var updatedModel= RunFiltersMethod(model)
return View(updatedModel);
}
Now, in this case Page2 renders properly from (Point 1) with all values passed in above from the GrabDataMethod. However, when I POST for Page2 the Page2Model I receive at (Point 2) has none of the original entries, e.g. everything not modified directly by Page2 itself is null or default (in fact it seems the model from the post method is a new model entirely). I've made a horrible workaround for the time being, but I need a proper fix, is there any reason that this would be happening?
Page2 View Code
#model Mvc2013.Models.Page2Model
#using (Html.BeginForm("Page2", "Controller"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
<div class="row">
<div class="col-md-12">
#Html.Kendo().Chart(<!-- code removed, this is working -->)
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="col-md-8">
#Html.LabelFor(model => model.Prop1, new { #class = "control-label" })
</div>
<div class="col-md-4">
#Html.EditorFor(model => model.Prop1, new { #class = "control-label" })
#Html.ValidationMessageFor(model => model.Prop1)
</div>
</div>
<div class="col-md-3">
<div class="col-md-8">
#Html.LabelFor(model => model.Prop2, new { #class = "control-label" })
</div>
<div class="col-md-4">
#Html.EditorFor(model => model.Prop2, new { #class = "control-label"})
#Html.ValidationMessageFor(model => model.Prop2)
</div>
</div>
</div>
<!-- This carries on similarly for lots more attributes -->
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Inside the Beginform you should add #Html.HiddenFor(model => model.Something) where the Something are the properties which you want back on the postback.
If you don't then the property values will be default values.

Categories