Skip to content

v0.3.0

Compare
Choose a tag to compare
@thomashoneyman thomashoneyman released this 22 Oct 16:22
1a8504d

Formless 0.3.0 introduces a number of significant changes and improvements to the library. These include:

  • Breaking: Simplified the component by removing the submitter function and the output type parameter. Now, on submission, you simply receive the output fields of form you put into the component, and you can transform it after the fact exactly as you would have done with the submitter function previously.
  • Breaking: Renamed the inputs component input field to initialInputs to better reflect what it is used for.
  • Added a noValidation function which you can provide as your validator function when you do not require any form validation
  • Improved the Send query so that you can send actions AND requests to an external component within Formless, instead of only being able to send actions. This now has parity with Halogen’s query and query’ functions.
  • Breaking: Updated the Modify query to actually take a modification function input -> input, instead of simply setting the input. Now, the query helpers set and setValidate provide the old modify behavior, and the query helpers modify and modifyValidate accept a modification function. In addition, this allows you to set a field to touched and validate it without updating its input with modifyValidate_ fieldName identity.
  • Added new SetAll and ModifyAll queries, which allow you to set or modify all input fields in your form at once.
  • Added new helper queries for every public query in the library, namespaced under a new Formless.Query module
  • Possibly breaking: Restructured the underlying modules, while still re-exporting everything from a top-level Formless module. If you previously used import Formless as F, this will not break any code.

Migrating from 0.2.0 to 0.3.0

Most of these changes are new capabilities that expand the library or under-the-hood improvements that won’t affect existing code. However, there have been breaking changes. Most importantly, #26 removed the submitter function and the output type parameter. In addition, several modules have been restructured; any users who simply import Formless as F will be unaffected, but users who import from specific modules will need to update their imports.

Handling the new submission format

To update code that previously used the submitter function, you’ll need to take three actions:

1. Remove the output type from all Formless types

The output type is no longer used in Formless. Instead, you should transform your form type into your desired output after receiving the Submitted message.

-- In old code, Formless took both the form type and the
-- type you wanted to parse to after submission.
data Query a = Formless (F.Message' ContactForm Contact) a
type ChildQuery = F.Query' ContactForm Contact Aff

-- Now, Formless simply takes the form type.
data Query a = Formless (F.Message' ContactForm) a
type ChildQuery = F.Query' ContactForm Aff

2. Remove the submitter function from component input

The submitter function is no longer used. Instead, any manipulations you need to do to your form record after successful submission should be done in your eval handler for the Submitted message.

-- Previously, Formless took four inputs
{ inputs, validators, submitter, render }

-- Now, it no longer takes the submitter function, and `inputs`
-- has been renamed to `initialInputs`
{ initialInputs, validators, render }

3. Either delete the submitter function altogether, or move it to the handler for the Submitted message

The submitter function ended up being used mostly to unwrap output fields and nothing else, causing unnecessary clutter in the component type. Now, any transformations or actions you need to take on the form once submitted are simply done when you handle the Submitted message.

-- In old code, you didn't need to do anything to handle the
-- `Submitted` message because your submitter function had
-- already run.
eval (Formless (F.Submitted contact) a) = a <$ do
  H.liftEffect $ logShow (contact :: Contact)

-- Now, Formless won't transform your output beyond validating
-- it and taking the successfully-parsed values for each
-- field. You can simply move your submitter function to
-- this handler.
eval (Formless (F.Submitted formOutputs) a) = a <$ do
    let contact = F.unwrapOutputFields formOutputs
    H.liftEffect $ logShow (contact :: Contact)

Handling the new modify vs. set queries

In 0.2.0, the modify queries didn’t actually allow you to provide modification functions to run on a form field’s input. Instead, it simply set the value of the field. Now there is support for modify to take a modification function, and a new query, set, to perform the old setting behavior.

This means that ALL uses of modify and modifyValidate will need to be replaced with set and setValidate to preserve their behavior.

For example:

-- Old: uses modifyValidate to set the value of the field
-- to whatever the user enters
HE.onValueInput $ HE.input $ F.modifyValidate _name

-- New: uses setValidate instead
HE.onValueInput $ HE.input $ F.setValidate _name

However, this opens up new functionality. You no longer have to get current input, transform it, and then set it in the form; you can provide a modification function instead:

-- Old: Get the input out, modify it, then set it.
HE.onChange $ HE.input_ $ F.modify _enable (not $ F.getInput _enable form)

-- New: Simply provide the modification function you want
HE.onChange $ HE.input_ $ F.modify _enable not

It also enables a solution for #32: Force validation on non-dirty fields?. To force validation on a non-dirty field, you can use modifyValidate with identity as the modification function:

-- This will set a field to a dirty state and run validation
F.modify _fieldName identity

Handling the new send functionality

In 0.2.0, you could only send actions through Formless to components embedded within the form. Now you can send requests or actions freely. To support this, the Send query was updated:

-- Old
| Send cs (cq Unit) a

-- New
| Send cs (cq a)

You can continue to use send to send actions, but you can also send requests, too:

x <- H.query unit $ F.send Email (H.request Typeahead.GetItems)
_ <- H.query unit $ F.send Email (H.action Typeahead.Clear)

Uses of the send function should continue to work as-is, but uses of the send' function will no longer require H.action to be specified:

-- Old
H.query' CP.cp1 unit $ H.action $ F.send' ...

-- New
H.query' CP.cp1 unit $ F.send' ...

Handling renames and module restructuring

A number of modules have been restructured to better organize the library and make functionality clearer. If you use import Formless as F, none of these changes will affect you and your imports will work as usual. However, if you previously were importing from specific modules, you will need to update your imports.

The module changes include:

  1. The Formless module previously held all component types, query helpers, and the component itself, as well as re-exporting other modules. Now, the Formless module simply re-exports other modules. The component types live in Formless.Types.Component, the query helpers live in Formless.Query, and the component lives in Formless.Component.
  2. The Formless.Spec module contained all form types, plus lenses to access them. Now, this module has been split so that the types live in Formless.Types.Form and the lenses live in Formless.Retrieve. The Formless.Retrieve module also contains functions that were previously in Formless.Spec.Transform but are used to access fields in the form, along with several new helper functions.
  3. The Formless.Spec.Transform and Formless.Internal modules have been split into several modules. Internal transformations have been moved to Formless.Transform.Internal. All transformations that operate on a row type are in Formless.Transform.Row, and all transformations that operate on a record type are in Formless.Transform.Record. Functions that are not actually transformations but are simply ways to retrieve values from the form have been moved to Formless.Retrieve.
  4. All other modules have remained untouched and operate as before.