如何为动态生成的内容编写自定义表单助手模板?

Posted

技术标签:

【中文标题】如何为动态生成的内容编写自定义表单助手模板?【英文标题】:How to write a custom form helper template for dynamically generated content? 【发布时间】:2015-08-29 15:20:52 【问题描述】:

我有某种测验系统,向用户显示一个问题和几个带有单选按钮的答案选项。

但是当我使用一个通过列表填充的 inputRadioGroup 的帮助器时,它看起来不再漂亮(如 Twitter Bootstrap)。单选按钮是内联的,而它们应该在彼此下方。实际上我想将图标更改为更漂亮的按钮。

现在是这样的:

因此,我尝试编写自己的自定义表单助手,但一直卡住。我发现很难理解这方面的文档: https://www.playframework.com/documentation/2.3.x/JavaFormHelpers

首先我创建了一个名为 myFieldConstructorTemplate.scala.html

的新模板
@(elements: helper.FieldElements)

<div class="@if(elements.hasErrors) error">
<label for="@elements.id">@elements.label</label>
<div class="input">
    @elements.input
    <span class="errors">@elements.errors.mkString(", ")</span>
    <span class="help">@elements.infos.mkString(", ")</span>
</div>
</div>

将其保存到 /views 文件夹。然后尝试在我的视图类中使用它 quiz.scala.html:

@import helper._
@import helper.twitterBootstrap._

@(questionList: List[Question], answerList: List[Answer], answerRadioForm: Form[Answer])

@helper.form(action = routes.Application.nextQuizPage(), 'id -> "answerRadioForm")
    @helper.inputRadioGroup(
    answerRadioForm("Answer"),
    options = answerList.map(answer => answer.answerID.toString -> answer.answerText),
    '_label -> "Answer",
    '_error -> answerRadioForm("answerID").error.map(_.withMessage("select answer")))
<button type="submit" class="btn btn-default" value="Send">
    Next Question
</button>

@implicitField = @ FieldConstructor(myFieldConstructorTemplate.f) 
@inputText(answerRadioForm("questionID"))

如果我把它放到我的模板中,我会得到一个not found: value implicitField-error。

那么,我怎样才能设法将单选按钮的外观更改为下方并看起来像 Twitter Bootstrap?

[EDIT1]:我已将导入顺序更改为建议的版本:

@(questionList: List[Question], answerList: List[Answer], answerRadioForm: Form[Answer])

@import helper._
@implicitField = @ FieldConstructor(myFieldConstructorTemplate.f) 
@import helper.twitterBootstrap._

然后我收到此错误:

ambiguous implicit values: 
both method implicitField of type => views.html.helper.FieldConstructor 
and value twitterBootstrapField in package twitterBootstrap of type 
=> views.html.helper.FieldConstructor match expected type 
views.html.helper.FieldConstructor

我认为这与我将答案导入单选按钮的方式有关?

[EDIT2]: 现在导入的顺序是:

@(questionList: List[Question], answerList: List[Answer], answerRadioForm: Form[Answer])

@import models.Question
@import models.Answer

@import helper._
@implicitField = @ FieldConstructor(myFieldConstructorTemplate.f) 

这样,程序就可以编译了。但是单选按钮看起来还是一样的。所以我试图改变设计,但不太奏效。现在看起来所有的单选按钮都融合成一个了:

这是我的模板类myFieldConstructorTemplate.scala.html

@(elements: helper.FieldElements)

<div class="btn-group", data-toggle="buttons">
<label for="@elements.id">@elements.label</label>
<div class="input">
    <label class="btn btn-primary">
    @elements.input
    <span class="errors">@elements.errors.mkString(", ")</span>
    <span class="help">@elements.infos.mkString(", ")</span>
</label>
</div>
</div>

[EDIT3]: 我已经根据to the last answer 更改了我的班级,但单选按钮仍然相互融合。所以我想指出,我并不执着于使用inputRadioGroup from the playframework helper,如果有另一种工作方式相同并且看起来几乎像引导程序的解决方案,我会很乐意使用它。似乎更改助手并不是那么容易/直观。我感谢任何形式的帮助!

【问题讨论】:

【参考方案1】:

implicitField-定义移动到文件顶部:

@(questionList: List[Question], answerList: List[Answer], answerRadioForm: Form[Answer])
@import helper._
@implicitField = @ FieldConstructor(myFieldConstructorTemplate.f) 
@import helper.twitterBootstrap._

@helper.form(action = routes.Application.nextQuizPage(), 'id -> "answerRadioForm")
    @helper.inputRadioGroup(
    answerRadioForm("Answer"),
    options = answerList.map(answer => answer.answerID.toString -> answer.answerText),
    '_label -> "Answer",
    '_error -> answerRadioForm("answerID").error.map(_.withMessage("select answer")))
<button type="submit" class="btn btn-default" value="Send">
    Next Question
</button>

@inputText(answerRadioForm("questionID"))

这确保隐式值在需要的地方可用。

【讨论】:

如果我这样做,我会在@(questionList: List[Question], answerList: List[Answer], answerRadioForm: Form[Answer]) 行中得到一个编译错误not found: value questionList。如果我将线 @implicitField = ... 移动到 @questionList:... 线下方,我会收到此错误:ambiguous implicit values: both value twitterBootstrapField in package twitterBootstrap of type =&gt; views.html.helper.FieldConstructor and method implicitField of type =&gt; views.html.helper.FieldConstructor match expected type views.html.helper.FieldConstructor 我试过你的版本,但一直出错,请参阅我编辑的帖子。【参考方案2】:

始终将参数保留在模板之上并删除以下导入语句@import helper.twitterBootstrap._这将与您自己的字段构造函数冲突。

@(questionList: List[Question], answerList: List[Answer], answerRadioForm: Form[Answer])

@import helper._
@implicitField = @ FieldConstructor(myFieldConstructorTemplate.f) 

希望它能解决您的问题。

【讨论】:

我也试过你的版本,但我一直收到错误。请查看我的编辑。 删除以下导入语句@import helper.twitterBootstrap._ 并尝试。 删除了 import 语句,现在我的程序可以编译了。但它看起来与我发布的图片完全相同。单选按钮仍然是内联的,文本仍然是粗体,看起来不像引导程序。 @hamena314 您所看到的是默认行为,为了按照您的方式进行操作,您需要包含相应的引导 css 选择器。 @hamena314 看看这个link 在 twitter bootstrap 中设置单选按钮。【参考方案3】:

Twitter Bootstrap 结构需要准确。

btn-group 类包裹在inputRadioGroup 助手周围,如下所示:

<div class="btn-group" data-toggle="buttons">
@helper.inputRadioGroup(
    answerRadioForm("Answer"),
    options = answerList.map(answer => answer.answerID.toString -> answer.answerText),
    '_label -> "Answer",
    '_error -> answerRadioForm("answerID").error.map(_.withMessage("select answer")))
</div>  

然后将模板替换为:

@(elements: helper.FieldElements)

<label class="btn btn-primary">
    @elements.input @elements.label
</label>

<span class="errors">@elements.errors.mkString(", ")</span>
<span class="help">@elements.infos.mkString(", ")</span>

一般来说,也许另一种方式会很有趣。当您使用fooForm("myField"...) 时,您可以在for 循环中使用fooForm("myField[@i]"...),其中@i 是一个从0 到有多少输入的计数器。然后,您可以自己勾勒出完整的 HTML,而不是使用隐式值。

顺便说一句,关于这一切的 Scala 版本的文档比 Java 版本包含更多的信息。见here。它比 Java 版本的文档包含更多关于 inputRadioGroup 的信息,但对于更好地理解这一切仍然非常有用。

一些 Play Bootstrap plugin 在 GitHub 上提供了代码,这些代码也很有用,尤其是因为它还使用隐式值。

更新

这里有几个 GitHub 项目展示了如何实现这一点:

使用 Scala 版本的 Play:https://github.com/bjfletcher/play-bootstrap-radio-group 使用 Java 版本的 Play:https://github.com/bjfletcher/play-java-bootstrap-radio-group

结果截图:

【讨论】:

我已将您的建议编辑到我的模板中,但仍然无法正常工作。这就是它现在的样子:i.imgur.com/7i5f85i.jpg 我发现自定义助手的文档对于像我这样的初学者来说非常混乱。文档中的更多示例或教程会有所帮助。我不知道 Scala,所以我不太了解那里的文档。在 github 示例中没有 cmets,即为什么 @containsReadonly 在那里?它是干什么用的?我怎样才能改变它?我需要改变它吗? ... 尤其是 scala、html 和 bootstrap 的混合很难掌握和理解。 嗨@hamena314,只需为您准备一个项目 - 请参阅更新的答案。希望能帮助到你。 :) GitHub 上现在有 Java 版本和 Scala 版本。查看更新的答案。 谢谢 - 我很高兴我们终于为你解开了僵局 - 欢迎来到 Play 网络平台的世界!关于您的问题 - 是的,您可以在不使用 Play 表单助手的情况下完成所有操作。例如,对于 GitHub 项目,将表单助手替换为:
,那么它就 100% 没有助手。在使用 Bootstrap 时,我发现最好为复杂的部分编写我们自己的,因为 Play form helpers 的 HTML 结构有时不能很好地与 Bootstrap 的预期结构配合使用。随时使用。享受:)
Re radio-group - 我想你在考虑按钮组 -> getbootstrap.com/javascript/#buttons-checkbox-radio(你可以使用与我的项目类似的技术来使用它)。

以上是关于如何为动态生成的内容编写自定义表单助手模板?的主要内容,如果未能解决你的问题,请参考以下文章

如何为自定义输入迭代器定义指针

如何为 Ejabberd 自定义模块生成文档?

如何为 Presto 编写自定义窗口函数?

如何为 NSManagedObject 子类编写自定义访问器?

如何为 C# 生成自定义 StyleCop 规则 - 条件编译预处理器指令

如何为事务管理编写自定义过滤器