I found myself repeatedly making form fields on my model that needed validation before sending the content to the server. I used @rtfeldman’s elm-validate library, which I really like, bit I also found myself adding extra logic and model fields for when that data had validations that were delayed for some amount of time (turns out the user doesn’t like being told that their email isn’t valid while they’re still busy typing it in ) So I made a package based on elm-validate, and taking a similar mental approach to remote-data that represents data on the model as data that needs validation, and properly handles the different possible states, including delayed validations. In practice this has cleaned up my code quite a bit.
At work, we’ve been using a different approach, (I think @stoeffel was wanting to open-source it, but hasn’t had time yet) where we have a Validation module that is basically some helper functions that work with data -> Result (List error) validatedData (and help compose them in a way that’s similar to json-decode-pipeline. We only store the unvalidated data in the Model, and we define a separate ValidatedModel type that’s produced if validation is successful.
Interesting! I’ve been using the same approach at work for validation, and it’s been really nice. I like the simplicity of only storing raw user input in the model, and validating as needed, with just a few helper functions.
For pipelining, I’ve an awkwardly named
applyOrAccumulate : Result x a -> Result (List x) (a -> b) -> Result (List x) b
function to use as you described.
formToData : form -> Result (List error) Data
formToData form =
Ok Data
|> applyOrAccumulate (form.email |> validateEmailString)
|> applyOrAccumulate ...
Looking forward to seeing this kind of approach as a package.
I threw something together after some slack conversations the other week, but hadn’t considered accumulating the errors along the way. That sounds really interesting, I love the idea. Looking forward to seeing what @stoeffel came up with! <3
At work, we’ve been using a different approach, (I think @stoeffel was wanting to open-source it, but hasn’t had time yet) where we have a Validation module that is basically some helper functions that work with data → Result (List error) validatedData
Yes, that sounds very interesting! Is there any provision for delayed validations?
I’m not sure if I understand the question, but all validations are delayed, and you only run them at the places that you use the validated values. (If you wanted them not delayed, you’d have to use lazy to cache the results of the validation.)
One suggestion would be to allow for type transformations from input to output. I have recently been working on a project where we allow users to input monetary values via a text field. We want the users to be able to copy and past into the field with mistakes. Therefore allow any string as input. This is then converted to a number before being sent to the server. I often see that validation (a -> Result x b) is the same as this transformation. If you were to enable these transformations the Validatable type would likely change to:
Hey, looks cool. I recently started playing with some form ideas and used RemoteData as my inspiration. I also only fire validations on blur and submit, so will definitely check this out!
The one thing I’ve been thinking about was using Dict for form fields and a single Msg for updating fields. For larger apps/forms this seems like it could really help with productivity & less duplicate code (I’m not opposed to duplicate code in Elm typically, but if you are creating a lot of forms…)
I haven’t seen this approach taken yet in the libraries I’ve looked it. Is there a reason not to do it?
Thanks for the note, @joefiorini! The dict is a fine idea if you’re going to be making lots of forms and need a very flexible pattern. https://github.com/etaque/elm-form is a good library that takes this approach. But it just gives up some type-safety. So it’s a tradeoff. Great library, though!
@splodingsocks I really like your approach, but what would be a good way to ensure you don’t forget to add a new field to a button that needs all validadtions to pass?
I am thinking about it.
Would you agree that this boils down to elm not allowing to map over dicts or compare any kind of type?
I am not saying it should necessarily, but my current understanding is that you can either put validatables in a list for reusability (loosing type safety for access) or in a type alias (not being able to map over them).