jwaliszko / ExpressiveAnnotations

Annotation-based conditional validation library.
MIT License
351 stars 123 forks source link

Non-empty list client-side validation #147

Closed FilipFilipov closed 7 years ago

FilipFilipov commented 7 years ago

I have this model that contains a list of strings that I want to represent with a multiselect control, generated with the ListBoxFor helper and I want to make sure it's not empty. It's easy enough to do server-side with AssertThat("List != null"), but that doesn't work client-side. Is there any way to make it work?

jwaliszko commented 7 years ago

In this case you should probably use simple Required attribute. It's because AssertThat works for non-null fields, so it is not supposed to trigger in your case, even at server side, when list value is null.

Mabye what you want is to check not for null, but for emptiness instead. In such case you could write a custom method for that, e.g. ArrayLength, and use it (see web sample for similar case), e.g.

[Required]
[AssertThat("ArrayLength(List) > 0")]
public List List { get; set; }
FilipFilipov commented 7 years ago

I think that html helper might be bugged. Required doesn't do anything client side and neither does the IsNotEmpty method I wrote for EA. There are supposed to be extra data attributes added to the element, right? Because I'm not seeing them. Did you ever test anything with ListBoxFor?

jwaliszko commented 7 years ago

I cannot investigate this any further right now, because my notebook has crashed. Untill I get the new one, only advice I can give, is to look how much the rendered HTML differs from what DropDownListFor gives, and check the ValueParser attribute (in particular look at the definition for ArrayParser in Home.cshtml page of sample project).

jwaliszko commented 7 years ago

Just to confirm and close the question - for the list to be non-null, simple [Required] attribute is enough (no need to use EA):

[Required]
public string[] SelectedOptions { get; set; }

@Html.ListBoxFor(m => m.SelectedOptions, new SelectList(Model.Options, null), string.Empty)

If you need to assert that some specific amount of items needs to be selected, EA allows to do that in the following manner, e.g.

[Required]
[AssertThat("ArrayLength(SelectedOptions) > 1")]
[ValueParser("ArrayParser")]
public string[] SelectedOptions { get; set; }

where ArrayLength is your custom method (either registered in toolchain or available within the model context):

Toolchain.Instance.AddFunction<string[], int>("ArrayLength", array => array.Length);

ea.addMethod('ArrayLength', function(array) {
    return array.length;
});

and ArrayParser is your custom parsing mechanism to extract field values:

ea.addValueParser('ArrayParser', function(value, field) { // provide your custom parsing mechanism of the field value at client side
                                                          // parameters: value - raw data string extracted by default from DOM element
                                                          //             field - DOM element name for which parser was invoked
    var array = $(':input[name="' + field + '"]').serializeArray();
    return array.length === 0
        ? null    // if array is empty, return null for RequiredIf to complain
        : array;  // otherwise - return array object for the ArrayLength method to work on
});