1

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>
Ali Briceño
  • 1,096
  • 2
  • 17
  • 44

2 Answers2

2

In Foolproof you have those options for [RequiredIf(...)]:

[RequiredIf]
[RequiredIfNot]
[RequiredIfTrue]
[RequiredIfFalse]
[RequiredIfEmpty]
[RequiredIfNotEmpty]
[RequiredIfRegExMatch]
[RequiredIfNotRegExMatch]

In your case "Checkboxes" is a class, custom made, i suppose. You'll have to create a custom Attribute to validate this

OR...

You could add a Property on this class to return a boolean and use the RequiredIfTrue validator.

public class Checkboxes {
    public bool IsAtLeastOneSelected
    {
        get{
            return PaginasConsultadas.Any(r => r.ValorCheckBox == [WHATEVER_VALUE MEANS_CHECKED]);
        }
    }
}

and then...

[RequiredIfTrue("IsAtLeastOneSelected")]
[DisplayName("¿Para que usa esta(s) página(s)?")]
public string ParaQueUsaEstasPag { get; set; }

Your ViewModel is probably like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

public class TestViewModel
{
    public List<Checkboxes> PaginasConsultadas { get; set; }

    [RequiredIfTrue("IsAtLeastOneSelected")]
    public string ParaQueUsaEstasPag { get; set; }

    //You dont put that on your view. 
    //This is just used with RequiredIfTrue data anotation attribute so the Validator checks for it
    public bool IsAtLeastOneSelected
    {
        get { return PaginasConsultadas.Any(r => r.ValorCheckBox); }
    }
}

public class Checkboxes
{
    //In order to check/uncheck you should use boolean for ValorCheckBox
    public bool ValorCheckBox { set; get; }
    public string NombreCheckBox { set; get; }
}

The rest, [Foolproof configuration] you've got to check the documentation. There are some scripts you have to add to make it work.

http://foolproof.codeplex.com/ RequireIf Code = http://foolproof.codeplex.com/SourceControl/latest#Foolproof/RequiredIf.cs

Pedro Matos
  • 387
  • 2
  • 14
  • Can you help me to create a custom attribute? Because i do the second one but how i know if the user check or not in the view? – Ali Briceño Sep 10 '14 at 15:12
  • Can you edit your question to add more detail in how you did the View? This "IsAtLeastOneSelected" is just a readonly property to help you use the RequiredIftrue attribute. Please add the view code, so we can help you better. – Pedro Matos Sep 10 '14 at 15:18
  • So, i did what you suggested... The code: `public bool IsAtLeastOneSelected {get{bool HayAlgunaCheck =PaginasConsultadas.Any(r => r.ValorCheckBox == true);return HayAlgunaCheck;}}` works because i put a breakpoint and enters... but, there's not a validation :( – Ali Briceño Sep 10 '14 at 21:13
  • I see this in the render view: `` – Ali Briceño Sep 10 '14 at 21:56
  • My case its exactly this: [http://stackoverflow.com/questions/20764590/foolproof-requirediftrue-not-working-for-mvc5](http://stackoverflow.com/questions/20764590/foolproof-requirediftrue-not-working-for-mvc5) the client side is the problem. – Ali Briceño Sep 11 '14 at 02:39
  • So, you did what the post suggested? Activated the Client Validation on Web.Config? ClientValidationEnabled setting in web.config. Which javascript files you have in your sollution? – Pedro Matos Sep 11 '14 at 17:02
  • Yes Pedro, but nothing :( – Ali Briceño Sep 11 '14 at 17:56
  • Thanks for all help Pedro! – Ali Briceño Sep 13 '14 at 01:44
0

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]*;

});
levelnis
  • 7,665
  • 6
  • 37
  • 61
Elad Lachmi
  • 10,406
  • 13
  • 71
  • 133
  • Mmmm... i test this but i dont understand because i need to do the validation in the view, similary to Data Annotations error messages.... do you follow me? Thanks for the help my friend – Ali Briceño Sep 10 '14 at 14:48
  • I see, this solution will help you on the server-side, not the client. I will edit my answer with another solution. – Elad Lachmi Sep 11 '14 at 08:54
  • Question. What does the IEnumerable GetClientValidationRules? – Ali Briceño Sep 11 '14 at 18:55
  • And... i don't know what supposed to do here: var checkValue = `[Find element via jquery from the params.checkpropertinputname];` – Ali Briceño Sep 11 '14 at 19:22
  • Elad are you here? The code on GetClientValidationRules never executes. I place a breakpoint there and never stop... Any idea? – Ali Briceño Sep 12 '14 at 04:00
  • Sorry, was a little busy. So you put the attribute on one of your model properties and `GetClientValidationRules` does not get called? Does `IsValid` get called when you submit the form? – Elad Lachmi Sep 12 '14 at 16:01
  • Yes, it was called Elad.... I put a breakpoint on the IsValid and hits... but GetClientCalidationRules never.. – Ali Briceño Sep 12 '14 at 16:35
  • Elad, i just update my question with the recients changes that you already suggest to me... check it out my friend, and thanks very much for your time in this... – Ali Briceño Sep 12 '14 at 16:41
  • In the class you have in your update you are missing the `IClientValidatable` interface on the class. That is why the method is not getting called. – Elad Lachmi Sep 12 '14 at 16:58
  • Thanks my friend... where exaclty i must to put the code? – Ali Briceño Sep 12 '14 at 17:28
  • Forget it... `public class PagSeleccionadasValidation : ValidationAttribute, IClientValidatable` – Ali Briceño Sep 12 '14 at 17:32
  • So... now its work the GetClientCalidationRules. But on my browser does not. I update the code again with the JS code... – Ali Briceño Sep 12 '14 at 18:59
  • Well the text in square brackets is only a placeholder. You need to put in jQuery code to find the element. I don't know how your entire view looks. If you can show me the entire view (or maybe even just the relevant part) I can figure out what jQuery code you need. – Elad Lachmi Sep 12 '14 at 20:30
  • Ready Eldad! i quit a duplicated jquery-validation library and finally works! Thanks a lot... Mission acomplish! – Ali Briceño Sep 12 '14 at 21:04