Aurelia-validation using satisfies and when


#1

Does anyone have some working examples of using the when and satisfies custom validation rules?

I have the basics working, but when I need to do something more complex I am failing to get it working.

I have a field that if checked i want to be sure that they enter something in a text field.
In this case the model contains a boolean if fuel was purchased, and a string field for the content.
I want to check if fuel was purchased (true) and then make sure at least 1 character was entered into the text field.
Example:

        ValidationRules    
            .ensure('fuelAmount').required().minLength(1).when((o: PaveTimecardModel) => o.fuelPurchased)
            .withMessage("Purchased Fuel checked, no entry detected.")
            .on(PaveTimecardModel);

And example of the satisfies that I cannot get working is as follows.
I have a drop down to select the rental type that must not be the default value of Not selected (0).
I have secondary drop down Instructor that requires a selection if the rental type is dual (3).
In this case the first rule validates correctly if a selection is made adding and removing the validation message correctly, but when the selection is dual, it should call out a validation error if no instructor is selected, but its not.
example:

        ValidationRules
            .ensure('rentalTypeId').satisfies((val, o: PaveTimecardModel) =>
            {
                if (o.rentalTypeId === RENTAL_NOSELECTED)
                {
                    return false;
                }

                return true;
            })
            .then()
            .withMessage("Rental Type needs to be selected.")
            .ensure('instructorId').satisfies((val, o: PaveTimecardModel) =>
            {
                if (o.rentalTypeId === RENTAL_DUAL && o.instructorId === NOINSTRUCTOR)
                {
                    return false;
                }

                return true;
            })
            .withMessage("Rental Type [Dual] requires instructor to be selected.")
            .on(PaveTimecardModel);

Anything looking out of place here?
Thanks bunches!


#2

So after taking a step back, and doing some other stuff, came back to this and got some of it working as expected.

One thing not mentioned before is the requirement that on entry to the form all validation error messages are displayed, and as the user enters data they are cleared. Once all validation errors are removed the action SAVE is enabled.

Here is what I ended up with where the validation checks of one select element is dependent on another select element.

First validation requires the select element has a value other than NOTSELECTED
Second validation is dependent on the first select element’s value, and if the selection is RENTAL_DUAL requires a selection other then NOINSTRUCTOR

    ValidationRules
             .ensure('rentalTypeId').satisfies((val, o: PaveTimecardModel) =>
            {
                if (o.rentalTypeId === RENTAL_NOTSELECTED)
                {
                    return false;
                }

                return true;
            })
            .withMessage("Rental Type needs to be selected.")
            .then()
            .ensure('rentalTypeId').satisfies((val, o: PaveTimecardModel) =>
            {
                if (o.rentalTypeId === RENTAL_DUAL && o.instructorId === NOINSTRUCTOR)
                {
                    return false;
                }

                return true;
            })
            .withMessage("Rental Type [Dual] requires instructor to be selected.")
            .on(PaveTimecardModel);

So that works a treat!
Now my second scenario not so much yet.

I have a checkbox, that if checked, requires some type of entry into an input element.

So I have to check that the checkbox is checked, and then display an error if the input field is empty.
Here is my attempt:

    ValidationRules
            .ensure('fuelPurchased').satisfies((val, o: PaveTimecardModel) =>
            {
                if (!o.fuelPurchased) 
                {
                    return true;
                }
                else if (o.fuelAmount && o.fuelAmount.length > 0)
                {
                    return true;
                }

                return false;
            })
            .withMessage("Purchased Fuel checked, no entry detected.")
            .then()
            .ensure('fuelAmount').satisfies((val, o: PaveTimecardModel) =>
            {
                if (!o.fuelPurchased) 
                {
                    return true;
                }
                else if(o.fuelAmount && o.fuelAmount.length > 0)
                {
                    return true;
                }

                return false;
            })            
            .when((o: PaveTimecardModel) => o.fuelPurchased)
            .withMessage("Purchased Fuel checked, no entry detected.")

Now here is my problem.
If I check fuelPurchased, I get a nice message that the fuelAmount is still empty and requires input.

If I enter something into the input element, the validation message never clears. It doesn’t clear because the change event is happening on the input element(fuelAmount) and it was the checkbox(fuelPurchased) that displayed the validation message.

If I now clear the input element (fuelAmount) I get two messages that no input is selected.
If I uncheck fuelPurchased both validation errors are cleared as expected since I am using the when method.


So here is the question:

How can I initially set the validation error when first checked, and have it cleared when the users inputs data into the second property.

I am thinking I might have to create a custom rule that is ran in each dependent property that evaluates both at the same time. Is that the right way to do this? I find I always over complicate things when using Aurelia.


#3

How can I initially set the validation error when first checked, and have it cleared when the users inputs data into the second property.

Listen for events. The input change Event bubbles, so you can listen for all change events on your form. Revalidate errors runs through all errors and checks them again.

<form change.delegate="controller.revalidateErrors()">
...
</form>

#4

I #^@*! knew there was an easier way as always. Thanks Matt!

I don’t recall see the revalidateErrors() method anywhere in the documents or guides.
I did just find it buried down in the bottom of the API.


#5

So, I had created a Gist.run that I was going to use to show my issue, and then after getting it to work it was showing that it was my issue…:stuck_out_tongue:

I then forgot to save it and when I went to look for it today it was gone. Anyone else like me? lol I sure hope not.

Anyways, I recreated it and was attempting to use @davismj hint on revalidateErrors().
I didn’t actually end up using that yet, and fell back to just validate() on the bubbled change event and it handled my problem.

I have created a gist.run here to show my end result for the requirements I had.
Hopefully it will help someone else.


#6

Just when I think I am ahead, its another step back…
It appears that not all validation checks are returning a change event.

The select elements , and input element type=“checkbox” do, but input type=“text” do not.

Any idea how to get the validation to let those bubble?

edit: I have updated the gist to show this.


#7

Just to close this out…the issue was a custom attribute not setting bubbles on a triggered event.
See more here