Formless v4 questions (dirty state and reinitialize forms)

Hi!
I’d like to know what’s the best way to check for dirty forms state and to reinitialize forms with the “new” formless api (it took me a while to catch up…).

After a bit of tinkering (and some help from discord) I got to this solution, but I wanted to know if there’s a more appropriate way to get to the same result.

I have defined two helper functions:

data DirtyProps = DirtyProps

instance
  ( Eq a
  , TE.TypeEquals (Record (value :: a, initialValue :: a | tail)) (Record row)
  ) =>
  FoldingWithIndex DirtyProps (Proxy sym) Boolean (Record row) Boolean where
  foldingWithIndex DirtyProps _prop b a = b || val1 /= val2
    where
    val1 = TE.to <<< Record.get (Proxy :: Proxy "value") <<< TE.from $ a
    val2 = TE.to <<< Record.get (Proxy :: Proxy "initialValue") <<< TE.from $ a

data ReinitForm = ReinitForm

instance
  ( TE.TypeEquals (Record (value :: a, initialValue :: a, result :: Maybe (Either e o))) (Record row)
  ) =>
  Mapping ReinitForm a (Record row) where
  mapping ReinitForm val =
    TE.to { value: val, initialValue: val, result: Nothing }
    where
    _initialValue = Proxy :: Proxy "initialValue"
    _value = Proxy :: Proxy "value"
    _result = Proxy :: Proxy "result"

-- | Checks whether a Formless form is dirty.
isFormDirty :: forall r. HFoldlWithIndex DirtyProps Boolean { | r } Boolean => { | r } -> Boolean
isFormDirty r = hfoldlWithIndex DirtyProps false r

-- | Reinitializes a Formless form with new inputs values.
reinitForm = hmap ReinitForm

In this case I have a wrapper component that contains a form. A lot of code omitted and simplified to highlight the important parts:

component = 
  Hooks.component \{slotToken } input -> Hooks.do
    Hooks.pure do
      HH.slot _form unit (form input) mempty (handleForm slotToken)

  where
  handleForm slotToken = case _ of
    Submit formRes -> do
      newInput <- someLogicAndAjaxCall
      Hooks.tell slotToken _form unit (Update newInput)

form input = F.formless { ... } input $ H.mkComponent
   ...

  where
  handleQuery = case _ of
    F.Query (Update newInput a) -> do
      { context: { formActions } } <- H.get
      handleAction $ formActions.setFields $ reinitForm newInput
      pure $ Just a
   F.Validate changed reply -> do
     { context: { fields } } <- H.get
     when isFormDirty fields $ do
        --  do some stuff like onbeforeunload
    pure $ Just $ reply $ F.validate changed etc. 
1 Like

Did you get any further with this? Neat solution, but as it relies on Eq instances for all values this might not work in all cases, in my case Files.

I wonder if it could be useful to have an extra property in FormState like touched that could be set to true if any fields change. Of course this wouldn’t capture the case where the user undoes a change, but it could be a simple solution for the sake of onbeforeunload :thinking:

The “result” key will be Nothing if the field has never been validated:

Alternately, you can see if initial field value and its current field value are different from each other.

Oh, sorry, I misinterpreted this as being about a single field but you mean the entire form. I think it’s reasonable that there is an “anyTouched” field in form state.

1 Like

Added a simple impl of anyTouched here: formState.anyTouched by noisyscanner · Pull Request #93 · thomashoneyman/purescript-halogen-formless · GitHub