haskellreflex

Dynamic parent element


I have this piece of code and it works perfectly fine. It toggles some styles on the text input field depending on the field value.

numberInput :: (MonadWidget t m) => m (Dynamic t (Maybe Double))
numberInput = divClass "form-group" $ do
  let errorState = "style" =: "border-color: red"
      validState = "style" =: "border-color: green"
  rec n <- textInput $ def & textInputConfig_inputType .~ "number"
                           & textInputConfig_initialValue .~ "0"
                           & textInputConfig_attributes .~ attrs
      let result = fmap (readMay . unpack) $ _textInput_value n
          attrs  = fmap (maybe errorState (const validState)) result
  return result

I ran into some problems making the parent element dynamic. I'd like to toggle the styles on the parent element of text input. I'd like to write something like but failed!

numberInput :: (MonadWidget t m) => m (Dynamic t (Maybe Double))
numberInput = do
  rec 
      dynAttrs <- -- reference to textInput
      elDynAttr "div" dynAttrs $ do
        n <- textInput $ def & textInputConfig_inputType .~ "number"
                       & textInputConfig_initialValue .~ "0"
        ...

Thanks for helping out!


Solution

  • Here is a little program where the attributes (align right or left) of the parent element depend on the state of a child element:

    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE RecursiveDo #-}
    import Reflex.Dom
    import qualified Data.Text as T
    import Safe
    
    main :: IO ()
    main = mainWidget bodyElement 
    
    bodyElement :: MonadWidget t m => m ()
    bodyElement =  el "div" $ do
        el "h2" $ text "Dynamic parent element"
        numberInput
        return ()
    
    numberInput :: (MonadWidget t m) => m (Dynamic t (Maybe Double))
    numberInput =  do
        rec
           let errorState = ("align" :: T.Text) =: "right"
               validState = "align" =: "left"
           let result = fmap (readMay . T.unpack) $ _textInput_value n 
               attrs  = fmap (maybe errorState (const validState)) result
           n <- elDynAttr "div" attrs $ do
              n1 <- textInput $ def & textInputConfig_inputType .~ "number"
                                    & textInputConfig_initialValue .~ "0"
    
              text "Some additional control"
              return n1
        return result
    

    As user user2407038 mentions, we have to return the numeric text element from an inner scope to the outer scope. Then we can use recursive do to access the element we define later in the program.

    As soon as you type a letter into the numeric field, it will jump to the right.