2

I have a field in a form that I need to be required if a checkbox in the same form is checked.

There doesn't seem to be a way to do this with regular model validation, so I created a remote validation method in my controller.

The problem is, since the field isn't required always, the validation doesn't even fire if I put it on the field. So I tried putting the validation on the checkbox, and now I get a different problem where the validation doesn't fire when I add text to the field.

Is there a way to do what I'm needing with custom validation, or do I need to do something in JavaScript? If so, what do I need to do?

Form:

<form>
   <input type="checkbox" asp-for="NotRecommended" checked=@Model.NotRecommended /> <label>Not Recommended</label>
   <textarea class="form-control" id="Notes" asp-for="Notes"></textarea>
   <span asp-validation-for="NotRecommended" class="text-danger"></span>
</form>

Model:

public class DetailsViewModel
{
    [DisplayName("Not Recommended")]
    [Remote("RequireNoteForFlag", AdditionalFields = "Notes", ErrorMessage = "Note is required when flagging someone.")]
    public bool NotRecommended { get; set; }

    [DataType(DataType.MultilineText)]
    [MaxLength(1500)]
    [DisplayName("Notes")]
    public string Notes { get; set; }
}

Remote Validator

public IActionResult RequireNoteForFlag(bool NotRecommended, string Notes)
{
    if (NotRecommended && String.IsNullOrWhiteSpace(Notes)) return Json("Note is required when flagging an Assessor");
    else return Json(true);
}
Kristen Hammack
  • 389
  • 5
  • 16
  • have you looked at this answer : https://stackoverflow.com/a/16100455/1875256 – Ehsan Sajjad Feb 15 '18 at 14:27
  • @EhsanSajjad oh lord, if that's the only way to do it, I might just tell my boss I can't... I wasted 3 days the last time I tried to write a custom validator. – Kristen Hammack Feb 15 '18 at 14:32
  • then use client side validation using jquery – Ehsan Sajjad Feb 15 '18 at 14:36
  • 1
    I implemented a solution for this problem with a [custom attribute](https://github.com/joeaudette/cloudscribe/blob/master/src/cloudscribe.Web.Common/DataAnnotations/RequiredWhenAttribute.cs) and [custom js for client side validation](https://github.com/joeaudette/cloudscribe/blob/master/src/cloudscribe.Web.Common/js/cloudscribe-validation-requiredwhen.js) – Joe Audette Feb 15 '18 at 14:39
  • back in the mvc 3 days, there was a library called MVC Foolproof Validation that worked pretty well.. maybe it's still useful https://archive.codeplex.com/?p=foolproof – JamieD77 Feb 15 '18 at 15:14
  • @JoeAudette could you tell me where to find an example of using the attribute? I thought I had it set up right, but it's not working, so I guess not... – Kristen Hammack Feb 15 '18 at 16:29
  • 1
    @KristenHammack I'm using it in my [RegisterViewModel](https://github.com/joeaudette/cloudscribe/blob/master/src/cloudscribe.Core.Web/ViewModels/SiteUser/RegisterViewModel.cs) to require a user to check the agreement checkbox if a hidden field is true. It might require some modification to the js for it to work based on the checked property of another checkbox. You can see my [partial view here](https://github.com/joeaudette/cloudscribe/blob/master/src/cloudscribe.Core.Web.Views.Bootstrap3/Views/Account/RegisterAgreementPartial.cshtml) – Joe Audette Feb 15 '18 at 16:46
  • @JoeAudette you're absolutely right. The value of a checkbox element apparently has nothing to do with whether it is checked or not. – Kristen Hammack Feb 15 '18 at 16:59
  • @JoeAudette if you add your links into an answer, I'll accept it. I had to tweak the javascript; should I put that into the question or edit your answer to add my tweaks? – Kristen Hammack Feb 15 '18 at 18:29
  • Use a conditional validation attribute, for example a [foolproof](https://github.com/leniel/foolproof) `[RequiredIf]` attribute so that you get both client side and server side validation. And if you want to write your own, refer [The Complete Guide To Validation In ASP.NET MVC 3 - Part 2](https://www.devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-2). And note your ``[Remote]` attribute (even if you implemented it correctly) only give client side validation which can easily be bypassed. –  Feb 15 '18 at 21:54
  • But if this is for `asp.net-core-mvc` (please tag you question correctly), then refer [Introduction to model validation in ASP.NET Core MVC](https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation) –  Feb 15 '18 at 21:58
  • And remove your `checked=@Model.NotRecommended` from the input. You never set the `checked` attribute when using the TagHelper (and `checked="true"` or `checked="false"` or `checked="anything"` all mean exactly the same thing - the checkbox will be checked because its a boolean attribute –  Feb 15 '18 at 22:02

2 Answers2

1

Joe gave me pretty much the whole answer in the comments, but hasn't posted it as an answer, so I'll post it for anyone else who might need to do this.

Create the Attribute https://github.com/joeaudette/cloudscribe/blob/master/src/cloudscribe.Web.Common/DataAnnotations/RequiredWhenAttribute.cs

Joe's Client-Side Validation https://github.com/cloudscribe/cloudscribe/blob/master/src/cloudscribe.Web.StaticFiles/js/cloudscribe-validation-requiredwhen.js

Modified Client-Side Validation

jQuery.validator.addMethod("requiredwhen", function (value, element, param) {
    var otherPropId = $(element).data('val-other');
    if (otherPropId) {
        var otherProp = $(otherPropId)[0].checked;
        if (otherProp) {
            return element.value.length > 0;
        }
    }
    return true;
});
jQuery.validator.unobtrusive.adapters.addBool("requiredwhen");

View

<input type="checkbox" asp-for="NotRecommended" /> <label asp-for="NotRecommended"></label>
<textarea class="form-control" id="Notes" asp-for="Notes"></textarea>
<span asp-validation-for="NotRecommended" class="text-danger"></span>

Model

[DisplayName("Not Recommended")]
public bool NotRecommended { get; set; }

[DataType(DataType.MultilineText)]
[MaxLength(1500)]
[DisplayName("Notes")]
[RequiredWhen("NotRecommended", true, AllowEmptyStrings = false, ErrorMessage ="A note is required when flagging an Assessor.")]
public string Notes { get; set; }
Kristen Hammack
  • 389
  • 5
  • 16
  • The second link is dead. – Haytam Apr 28 '19 at 09:08
  • Note the attribute code linked is .Net Core. .net Framework 4.7 requires different attribute code and slightly different jQuery validator code as the .Net 4.7 attributes output different data attributes. – Roberto Bonini Sep 22 '22 at 10:46
-1

I have implemented this with a custom validator in a similar way. However I applied the annotation to the field that would be required and included a condition in the arguments, eg:

[RequiredIf("NotRecommended = true")]
public string Notes { get; set; }

But, as you may already be aware, there is no way to cross-reference another property in data annotations so you will need to build in the javascript to handle this.

Unobtrusive validation adds a data-val="true" and data-val-required="error message" attribute to the input element so adding this in via javascript when the checkbox is checked will work.

You will also need to reinitialise the form validation so that the inputs with newly applied validation attributes are added.

Steve Harris
  • 5,014
  • 1
  • 10
  • 25
  • It's actually not true that there is no way to cross-reference properties. My current solution does that; the only reason it doesn't work is that the validation rule isn't getting fired. – Kristen Hammack Feb 15 '18 at 14:50
  • 1
    Either way, I think the rule should be applied to the notes field rather than the checkbox. – Steve Harris Feb 15 '18 at 14:53
  • It would be useful if you posted the custom validator rather than just the usage – cfbd Dec 07 '20 at 01:45