我有一个带有由控制器动态生成的任意数量输入的表单的视图。提交表单时,每个输入都应创建自己的记录,因此,如果有 60 个输入,则应创建 60 条记录。
应如何验证每个输入/字段?在 IHP 文档的示例中,单个表单仅创建 1 条记录,因此我不确定执行此操作的最佳或惯用方法是什么。
也许我可以将如下所示的函数映射到每个提交的输入,但是 Left 情况将由第一个验证失败而不是所有验证失败触发,因此我需要在重定向到前一个之前将每个失败保存在列表中(?)表单视图。
action CreatePostAction = do
let post = newRecord @Post
post
|> fill @'["title", "body"]
|> validateField #title nonEmpty
|> validateField #body nonEmpty
|> ifValid \case
Left post -> render NewView { post }
Right post -> do
post <- post |> createRecord
setSuccessMessage "Post created"
redirectTo PostsAction
Run Code Online (Sandbox Code Playgroud)
小智 5
尝试这样的事情:
action CreatePostAction = do
let titles :: [Text] = paramList "title"
let bodys :: [Text] = paramList "body"
let posts = zip titles bodys
|> map (\(title, body) -> newRecord @Post
|> set #title title
|> set #body body
|> validateField #title nonEmpty
|> validateField #body nonEmpty
)
validatedPosts :: [Either Post Post] <- forM posts (ifValid (\post -> pure post))
case Either.partitionEithers validatedPosts of
([], posts) -> do
createMany posts
setSuccessMessage "Post created"
redirectTo PostsAction
(invalidPosts, validPosts) -> render NewView { posts }
Run Code Online (Sandbox Code Playgroud)
为此,您需要这样的视图:
action CreatePostAction = do
let titles :: [Text] = paramList "title"
let bodys :: [Text] = paramList "body"
let posts = zip titles bodys
|> map (\(title, body) -> newRecord @Post
|> set #title title
|> set #body body
|> validateField #title nonEmpty
|> validateField #body nonEmpty
)
validatedPosts :: [Either Post Post] <- forM posts (ifValid (\post -> pure post))
case Either.partitionEithers validatedPosts of
([], posts) -> do
createMany posts
setSuccessMessage "Post created"
redirectTo PostsAction
(invalidPosts, validPosts) -> render NewView { posts }
Run Code Online (Sandbox Code Playgroud)
我的NewPostAction看起来像这样:
module Web.View.Posts.New where
import Web.View.Prelude
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A
data NewView = NewView { posts :: [Post] }
instance View NewView where
html NewView { .. } = [hsx|
<nav>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href={PostsAction}>Posts</a></li>
<li class="breadcrumb-item active">New Post</li>
</ol>
</nav>
<h1>New Post</h1>
<form id="main-form" method="POST" action={CreatePostAction}>
<input type="submit" class="btn btn-primary"/>
{forEach posts renderForm}
</form>
|]
renderForm :: Post -> Html
renderForm post = [hsx|
<div class="form-group">
<label>
Title
</label>
<input type="text" name="title" value={get #title post} class={classes ["form-control", ("is-invalid", isInvalidTitle)]}/>
{titleFeedback}
</div>
<div class="form-group">
<label>
Body
</label>
<input type="text" name="body" value={get #body post} class={classes ["form-control", ("is-invalid", isInvalidBody)]}/>
{bodyFeedback}
</div>
|]
where
isInvalidTitle = isJust (getValidationFailure #title post)
isInvalidBody = isJust (getValidationFailure #body post)
titleFeedback = case getValidationFailure #title post of
Just result -> [hsx|<div class="invalid-feedback">{result}</div>|]
Nothing -> mempty
bodyFeedback = case getValidationFailure #body post of
Just result -> [hsx|<div class="invalid-feedback">{result}</div>|]
Nothing -> mempty
Run Code Online (Sandbox Code Playgroud)