DEV Community

Discussion on: Moving from elm-validate to elm-verify

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

Well, I compared to what we were doing for validation functions, and it is almost the same. We use simple functions that we wrote ourselves.

validationResult =
    Ok SanitizedData
        |> verify (Ensure.nonEmptyOr (NameRequired, "Name required") x.name)
        |> verify (Ensure.intOr (NumberRequired, "Number required") x.num)
        |> verify customFieldValidation

-- OR with custom operator
-- +* is supposed to mean combine on the left (errors), apply on right

validationResult =
    Ok SanitizedData
        <+*> Ensure.nonEmptyOr (NameRequired, "Name required") x.name
        <+*> Ensure.intOr (NumberRequired, "Number required") x.num
        <+*> customFieldValidation

For my ensure functions, I put the value last so it is chainable:

percentResult x =
    Ensure.nonEmptyOr (ScoreField, "Score required") x
        |> Result.andThen (Ensure.intOr (ScoreField, "Score is not a number"))
        |> Result.andThen (Ensure.rangeOr (ScoreField, "Score needs to be 0 to 100") 0 100)

-- OR with standard operator

percentResult x =
    Ensure.nonEmptyOr (ScoreField, "Score required") x
        >>= Ensure.intOr (ScoreField, "Score is not a number")
        >>= Ensure.rangeOr (ScoreField, "Score needs to be 0 to 100") 0 100

I really need to just take the ending "Or" off the names.

Collapse
 
michaeljones profile image
Michael Jones

Thanks for the response. That looks great! The elm-verify API allows you to chain checks with andThen but your API looks very nice too. I suspect you might have more experience with Haskell than I do, given your <+*> operator :)

I'm curious about your first comment. It sounds like you validate everything on any change from the user. How do you avoid showing errors on empty fields that the user hasn't reached yet when they fill in the first field? Is validation only triggered after the first save event?

I've got some forms where I check for changes before enabling the save button but I haven't tied in the validation yet. Interesting stuff!

Thread Thread
 
kspeakman profile image
Kasey Speakman • Edited

I have a passing familiarity with Haskell, but haven't written in it yet. I use Haskell as a reference when I'm looking for a specific kind of function or operator. I couldn't actually find one for combine errors and apply ok at same time, so I made up the <+*>. In general the inline operators just help you eliminate parenthesis and make things shorter.

...
    |> Result.andThen ( ... ... )
-- turns into
...
    >>= ... ...

Turns out I don't use the <+*>, though. Even the few very common operators we do use in the code base (e.g. >>=) can confuse people (including people such as Future Me), much less a custom one. So I just use the verify function and suffer with parenthesis.

As far as validating forms, here is the strategy I use:

  • Instead of folding errors onto a general Field type (e.g. NameError), I name each error specifically. E.g. NameBlank, NameTooLong, NameAlreadyTaken, etc.

  • In the edit view, I light up fields red on any error.

-- Name field
    ... 
    div [ class "ui horizontal relaxed divided list" ]
       (showErrors [ NameBlank, NameTooLong, NameAlreadyTaken ] m.state.errors)
  • In the add view, I show errors on everything but blank errors. In other words, blank is not an error as far as the view is concerned. (But because it is internally an error, it will prevent the form from being submitted.)
-- Name field
    ...
    div [ class "ui horizontal relaxed divided list" ]
       (showErrors [ NameTooLong, NameAlreadyTaken ] m.state.errors)

I can indicate required fields with asterisks or some other affordance. I have a form where if any field is blank, I display a single message "All fields required" beside the Save button. It doubles for an instructive message as well as an answer to the question later: "Why can't I Save this?"

This allows me to avoid any complicated form state management whether it was before or after the user first typed. If you think about it, they will be just as scared when they blank out a field they typed in and it turns red as they will if it were red to start with. Because I just told them by turning it red that what they did was wrong. When it probably wasn't. They can blank it out as much as they want as long as something is in there before they hit Save.

For edit forms, I will validate and display errors even right after the form is loaded. For most data, this will not be a problem as it has already been validated when saved. But it could be that older validations allowed data through that newer ones consider errors. A human probably must correct the data or else it would have been back-filled already. So I'm ok with lighting up a field red immediately on load. That way the user knows what they are getting into if they want to edit that data. And it provides a built-in process to correct data.

Thread Thread
 
michaeljones profile image
Michael Jones

Thanks for the break down. Interesting to read. I think I'm still comfortable with the 'validate on save' approach but I appreciate learning about other strategies.