[英]Purescript Halogen manually trigger input validation outside of a form
I have input fields which I have marked with a required
attribute, but can't figure out a way to trigger a validation check (I am not working inside of a form, so using a default submit button action won't work for me). 我有一些用required
属性标记的输入字段,但无法找到触发验证检查的方法(我不在表单内部工作,因此使用默认的提交按钮操作对我来说不起作用) 。
A quick pursuit search shows many validity functions for core html element types, but I'm not sure how to apply these to Halogen. 快速追踪搜索显示了许多针对html核心元素类型的有效性函数,但是我不确定如何将其应用于Halogen。
Is there some way to trigger a DOM effect to check all required inputs on the page and get a result back? 是否有某种触发DOM效果的方法来检查页面上所有必需的输入并返回结果?
Here is an example component showing what I'm trying to achieve 这是一个示例组件,显示了我要实现的目标
import Prelude
import Data.Maybe (Maybe(..))
import Halogen as H
import Halogen.HTML as HH
import Halogen.HTML.Events as HE
import Halogen.HTML.Properties as HP
data Message = Void
type State =
{ textValue :: String
, verified :: Boolean
}
data Query a = ContinueClicked a | InputEntered String a
inputHtml :: State -> H.ComponentHTML Query
inputHtml state =
HH.div [ HP.class_ $ H.ClassName "input-div" ]
[ HH.label_ [ HH.text "This is a required field" ]
, HH.input [ HP.type_ HP.InputText
, HE.onValueInput $ HE.input InputEntered
, HP.value state.textValue
, HP.required true
]
, HH.button [ HE.onClick $ HE.input_ ContinueClicked ]
[ HH.text "Continue"]
]
verifiedHtml :: H.ComponentHTML Query
verifiedHtml =
HH.div_ [ HH.h3_ [ HH.text "Verified!" ] ]
render :: State -> H.ComponentHTML Query
render state = if state.verified then verifiedHtml else inputHtml state
eval :: forall m. Query ~> H.ComponentDSL State Query Message m
eval = case _ of
InputEntered v next -> do
H.modify $ (_ { textValue = v })
pure next
ContinueClicked next -> do
let inputValid = false -- somehow use the required prop to determine if valid
when inputValid $ H.modify $ (_ { verified = true })
pure next
initialState :: State
initialState =
{ textValue : ""
, verified : false
}
component :: forall m. H.Component HH.HTML Query Unit Message m
component =
H.component
{ initialState: const initialState
, render
, eval
, receiver: const Nothing
}
I don't think relying on HTML form validation is the most effective way of checking inputs within a Halogen application. 我认为依靠HTML表单验证不是检查Halogen应用程序中输入的最有效方法。 But I'll assume you have your reasons and present an answer anyway. 但我认为您有自己的理由,无论如何都要给出答案。
First things first, if we want to deal with DOM elements we need a way to retrieve them. 首先,如果要处理DOM元素,我们需要一种检索它们的方法。 Here's a purescript version of document.getElementById
这里的purescript版本document.getElementById
getElementById
:: forall a eff
. (Foreign -> F a)
-> String
-> Eff (dom :: DOM | eff) (Maybe a)
getElementById reader elementId =
DOM.window
>>= DOM.document
<#> DOM.htmlDocumentToNonElementParentNode
>>= DOM.getElementById (wrap elementId)
<#> (_ >>= runReader reader)
runReader :: forall a b. (Foreign -> F b) -> a -> Maybe b
runReader r =
hush <<< runExcept <<< r <<< toForeign
(Don't worry about the new imports for now, there's a complete module at the end) (现在不必担心新的导入,最后有一个完整的模块)
This getElementById
function takes a read*
function (probably from DOM.HTML.Types
) to determine the type of element you get back, and an element id as a string. 此getElementById
函数采用read*
函数(可能来自DOM.HTML.Types
)来确定您返回的元素的类型,以及元素ID作为字符串。
In order to use this, we need to add an extra property to your HH.input
: 为了使用它,我们需要向您的HH.input
添加一个额外的属性:
HH.input [ HP.type_ HP.InputText
, HE.onValueInput $ HE.input InputEntered
, HP.value state.textValue
, HP.required true
, HP.id_ "myInput" <-- edit
]
Aside: a sum type with a Show
instance would be safer than hard-coding stringy ids everywhere. 除了:带Show
实例的求和类型比在各处硬编码字符串id更安全。 I'll leave that one to you. 我将那个留给你。
Cool. 凉。 Now we need to call this from the ContinueClicked
branch of your eval
function: 现在,我们需要从eval
函数的ContinueClicked
分支中调用此函数:
ContinueClicked next ->
do maybeInput <- H.liftEff $
getElementById DOM.readHTMLInputElement "myInput"
...
This gives us a Maybe HTMLInputElement
to play with. 这给我们提供了一个Maybe HTMLInputElement
。 And that HTMLInputElement
should have a validity
property of type ValidityState
, which has the information we're after. 该HTMLInputElement
应该具有类型ValidityState
的validity
属性,该属性具有我们所需要的信息。
DOM.HTML.HTMLInputElement
has a validity
function that will give us access to that property. DOM.HTML.HTMLInputElement
具有一个validity
函数,该函数将使我们能够访问该属性。 Then we'll need to do some foreign value manipulation to try and get the data out that we want. 然后,我们需要进行一些外来的值操作,以尝试获取所需的数据。 For simplicity, let's just try and pull out the valid
field: 为了简单起见,让我们尝试拉出valid
字段:
isValid :: DOM.ValidityState -> Maybe Boolean
isValid =
runReader (readProp "valid" >=> readBoolean)
And with that little helper, we can finish the ContinueClicked
branch: 有了这个小帮手,我们就可以完成ContinueClicked
分支:
ContinueClicked next ->
do maybeInput <- H.liftEff $
getElementById DOM.readHTMLInputElement "myInput"
pure next <*
case maybeInput of
Just input ->
do validityState <- H.liftEff $ DOM.validity input
when (fromMaybe false $ isValid validityState) $
H.modify (_ { verified = true })
Nothing ->
H.liftEff $ log "myInput not found"
And then putting it all together we have... 然后将所有内容放在一起...
module Main where
import Prelude
import Control.Monad.Aff (Aff)
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log)
import Control.Monad.Except (runExcept)
import Data.Either (hush)
import Data.Foreign (Foreign, F, toForeign, readBoolean)
import Data.Foreign.Index (readProp)
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Newtype (wrap)
import DOM (DOM)
import DOM.HTML (window) as DOM
import DOM.HTML.HTMLInputElement (validity) as DOM
import DOM.HTML.Types
(ValidityState, htmlDocumentToNonElementParentNode, readHTMLInputElement) as DOM
import DOM.HTML.Window (document) as DOM
import DOM.Node.NonElementParentNode (getElementById) as DOM
import Halogen as H
import Halogen.Aff as HA
import Halogen.HTML as HH
import Halogen.HTML.Events as HE
import Halogen.HTML.Properties as HP
import Halogen.VDom.Driver (runUI)
main :: Eff (HA.HalogenEffects (console :: CONSOLE)) Unit
main = HA.runHalogenAff do
body <- HA.awaitBody
runUI component unit body
type Message
= Void
type Input
= Unit
type State
= { textValue :: String
, verified :: Boolean
}
data Query a
= ContinueClicked a
| InputEntered String a
component
:: forall eff
. H.Component HH.HTML Query Unit Message (Aff (console :: CONSOLE, dom :: DOM | eff))
component =
H.component
{ initialState: const initialState
, render
, eval
, receiver: const Nothing
}
initialState :: State
initialState =
{ textValue : ""
, verified : false
}
render :: State -> H.ComponentHTML Query
render state =
if state.verified then verifiedHtml else inputHtml
where
verifiedHtml =
HH.div_ [ HH.h3_ [ HH.text "Verified!" ] ]
inputHtml =
HH.div
[ HP.class_ $ H.ClassName "input-div" ]
[ HH.label_ [ HH.text "This is a required field" ]
, HH.input
[ HP.type_ HP.InputText
, HE.onValueInput $ HE.input InputEntered
, HP.value state.textValue
, HP.id_ "myInput"
, HP.required true
]
, HH.button
[ HE.onClick $ HE.input_ ContinueClicked ]
[ HH.text "Continue" ]
]
eval
:: forall eff
. Query
~> H.ComponentDSL State Query Message (Aff (console :: CONSOLE, dom :: DOM | eff))
eval = case _ of
InputEntered v next ->
do H.modify (_{ textValue = v })
pure next
ContinueClicked next ->
do maybeInput <- H.liftEff $
getElementById DOM.readHTMLInputElement "myInput"
pure next <*
case maybeInput of
Just input ->
do validityState <- H.liftEff $ DOM.validity input
when (fromMaybe false $ isValid validityState) $
H.modify (_ { verified = true })
Nothing ->
H.liftEff $ log "myInput not found"
getElementById
:: forall a eff
. (Foreign -> F a)
-> String
-> Eff (dom :: DOM | eff) (Maybe a)
getElementById reader elementId =
DOM.window
>>= DOM.document
<#> DOM.htmlDocumentToNonElementParentNode
>>= DOM.getElementById (wrap elementId)
<#> (_ >>= runReader reader)
isValid :: DOM.ValidityState -> Maybe Boolean
isValid =
runReader (readProp "valid" >=> readBoolean)
runReader :: forall a b. (Foreign -> F b) -> a -> Maybe b
runReader r =
hush <<< runExcept <<< r <<< toForeign
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.