Grettings my friends...
So i have a set of CheckBoxes that i set in my model:
[DisplayName("Páginas Consultadas")]
public List<CheckBoxes> PaginasConsultadas { get; set; }
And i have a fieldtext ("ParaQueUsaEstasPag") that is only required if any of checkboxes its checked...
[DisplayName("¿Para que usa esta(s) página(s)?")]
public string ParaQueUsaEstasPag { get; set; }
And part of the view:
<div class="col-lg-4" id="DivPagConsultadas">
<span>@Html.LabelFor(model => model.PaginasConsultadas, @Html.DisplayNameFor(model => model.PaginasConsultadas))</span>
@{
for (int i = 0; i < Model.PaginasConsultadas.Count(); i++)
{
<div class="checkbox">
<label>
@Html.CheckBoxFor(model => model.PaginasConsultadas[i].ValorCheckBox) @Model.PaginasConsultadas[i].NombreCheckBox
</label>
</div>
@Html.HiddenFor(model => model.PaginasConsultadas[i].valorRespuesta, new { @Value = @Model.PaginasConsultadas[i].valorRespuesta })
}
}
</div>
</div>
<br />
<div class="row">
<div class="col-lg-12">
@Html.LabelFor(model => model.ParaQueUsaEstasPag, @Html.DisplayNameFor(model => model.ParaQueUsaEstasPag))
@Html.TextAreaFor(model => model.ParaQueUsaEstasPag, 5, 1, new { @class = "form-control", placeholder = "Esta pregunta se responde con base en la respuesta de la pregunta anterior" })
@Html.ValidationMessageFor(model => model.ParaQueUsaEstasPag)
</div>
</div>
<br />
<div class="row">
<div class="col-lg-12">
<button type="submit" class="btn btn-default" onclick="dispararPleaseWait()">Enviar Encuesta...</button>
</div>
</div>
There's is a mode to do this using Foolproof (i.e [RequiredIf])?
Update: Follow the Elad idea, my class is the next:
public class PagSeleccionadasValidation : ValidationAttribute, IClientValidatable
{
//todo
private readonly String _ChkPagSel;
public PagSeleccionadasValidation(String ChkPagSel)
{
_ChkPagSel = ChkPagSel;
}
public string P {get; set;}
protected override ValidationResult IsValid(object value, ValidationContext validationcontext)
{
if (value == null)
{
var PropertyInfo = validationcontext.ObjectType.GetProperty(_ChkPagSel);
var Lista = (List<CheckBoxes>)PropertyInfo.GetValue(validationcontext.ObjectInstance, null);
bool HayAlgunaCheck = Lista.Any(r => r.ValorCheckBox == true);
if (HayAlgunaCheck)
{
return new ValidationResult(this.ErrorMessageString);
}
else
{
return ValidationResult.Success;
}
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.DisplayName),
ValidationType = "valpagselecc"
};
rule.ValidationParameters["valpag"] = P;
yield return rule;
}
}
In the js called "JS-ValPagSel.js" i put this:
$.validator.unobtrusive.adapters.addSingleVal('valpagselecc', 'valpag');
$.validator.addMethod('valpagselecc', function (value, element, params) {
//var checkValue = [Find element via jquery from the params.checkpropertinputname];
if (value) {
return false; // just for test
}
else {
return false;
}
});
And in the view:
<script src="/Scripts/jquery-1.10.2.js"></script>
<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script src="/Scripts/bootstrap.js"></script>
<script src="/Scripts/respond.js"></script>
<script src="/Scripts/blur.js"></script>
<script src="/Scripts/jquery-ui-1.11.1.js"></script>
<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js"></script>
<script src="/Scripts/Custom/JS-ValPagSel.js"></script>
<textarea class="form-control" cols="1" data-val="true" data-val-valpagselecc="El campo ¿Para que usa esta(s) página(s)? no es válido." data-val-valpagselecc-valpag="" id="ParaQueUsaEstasPag" name="ParaQueUsaEstasPag" placeholder="Esta pregunta se responde con base en la respuesta de la pregunta anterior" rows="5"></textarea>
What I would do is make the field [Required]
and in your action
check if one of the checkboxes is checked. If not, you can remove the specific required error from the ModelState
before checking ModelState.IsValid
.
Example:
if (Model.MyCheckBox.Checked)
{
ModelState.Remove("CheckboxName");
}
if(ModelState.IsValid)
{
//Do stuff...
}
Since you require client-side validation as well, I will add another option. You can use a custom data annotation. You need to create a class that inherits from ValidationAttribute
and implements IClientValidatable
.
public class RequiredIf : ValidationAttribute, IClientValidatable
{
private string _checkPropertyName;
public RequiredIf(string checkPropertyName)
{
_checkPropertyName = checkPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//Get PropertyInfo object
var phonePropertyInfo = validationContext.ObjectType.GetProperty(_checkPropertyName);
//Get value from property
bool checkValue = (bool)phonePropertyInfo.GetValue(validationContext.ObjectInstance, null);
if(!checkValue)
return new ValidationResult(this.ErrorMessageString);
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule rule = new ModelClientValidationRule();
rule.ErrorMessage = this.ErrorMessageString;
rule.ValidationType = "requiredIf";
rule.ValidationParameters.Add("checkpropertinputname", _checkPropertyName);
yield return rule;
}
}
This is a simplified example, but you can work along these lines. You can pass in to the constructor anything you like and pull all the data from the model via reflection, as in the example.
On the client side you need JS along these lines:
$.validator.unobtrusive.adapters.add('requiredIf', ['checkpropertinputname'],
function (options) {
options.rules['requiredIf'] = options.params;
options.messages['requiredIf'] = options.message;
});
$.validator.addMethod('requiredIf', function (value, element, params) {
var checkValue = *[Find element via jquery from the params.checkpropertinputname]*;
});