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

时间:2021-08-22 15:29:41

I have some sort of quiz system, where to user gets shown a question and several answer-options with radio-buttons.

我有一些测试系统,用户可以在那里看到一个问题和几个带有无线电按钮的答案选项。

But as I am using a helper for a inputRadioGroup that gets filled via a list, it does not look pretty anymore (like Twitter Bootstrap). The radiobuttons are inline, while they should be underneath each other. And actually I would like to change the icon to a prettier button.

但是,当我为一个通过列表填充的inputRadioGroup使用助手时,它看起来不再漂亮了(比如Twitter Bootstrap)。radiobutton是内联的,而它们应该在彼此的下面。实际上我想把图标换成更漂亮的按钮。

This is how it looks at the moment:

现在的情况是:

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

Therefore I tried to write my own custom form helper, but keep getting stuck. I find it frustratingly hard to understand the documentation for this: https://www.playframework.com/documentation/2.3.x/JavaFormHelpers

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

First I created a new template named myFieldConstructorTemplate.scala.html

首先,我创建了一个名为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>

Saved it to the /views-folder. Then try to use it in my view class quiz.scala.html:

保存到/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"))

If I put this into my template, I get a not found: value implicitField-error.

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

So how can I manage to change the appearance of my radiobuttons to underneath and looking like Twitter Bootstrap?

那么,我该如何改变我的radiobutton的外观,让它看起来像Twitter的Bootstrap呢?

[EDIT1]: I have changed the order of the imports to the suggested version:

[EDIT1]:我已将进口订单更改为建议版本:

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

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

I get this error then:

然后我得到这个错误:

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

I think this has to do with the way I import the answers into the radiobuttons?

我想这与我将答案导入到radiobutton的方式有关?

[EDIT2]: The order of the imports is now:

[EDIT2]:进口订单如下:

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

@import models.Question
@import models.Answer

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

With this, the program compiles. BUT the radiobuttons still look the same. So I tried to change the design, which does not quite work. It looks now like all the radiobuttons are melted into a single one:

有了这个,程序就可以编译了。但收音机的按钮看起来还是一样的。所以我试着改变设计,但这并不奏效。现在看来,所有的无线电按钮都被融化成一个单一的按钮:

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

Here is my template class myFieldConstructorTemplate.scala.html:

这是我的模板类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]: I have changed my class according to the last answer, but still the radiobuttons are melted into each other. So I want to point out that I am not fixated on using the inputRadioGroup from the playframework helper, if there is another solution that works the same and looks almost like bootstrap, I would gladly use that. It seems that changing the helper isnt that easy / intuitive. I appreciate any form of help!

[EDIT3]:根据最后的答案,我已经改变了我的班级,但仍然是无线电按钮相互融合。因此,我想指出的是,我并不专注于使用来自playframework helper的inputRadioGroup,如果有另一种类似bootstrap的解决方案,我很乐意使用它。似乎改变助手并不是那么简单/直观。我感谢任何形式的帮助!

3 个解决方案

#1


1  

The Twitter Bootstrap structure needs to be accurate.

Twitter的引导结构需要准确。

Wrap the btn-group class around the inputRadioGroup helper like so:

将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>  

Then replace the template with:

然后将模板替换为:

@(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>

In general, perhaps it'd be of interest another way of doing it. When you use fooForm("myField"...), you can use fooForm("myField[@i]"...) in a for loop where @i is a counter going from 0 to however many inputs there are. Then you can yourself sketch out the full HTML instead of using implicit values.

一般来说,也许用另一种方法来做会很有趣。当您使用fooForm(“myField”…)时,您可以在for循环中使用fooForm(“myField[@i]…”),其中@i是一个计数器,从0到任意输入。然后,您可以自己编写完整的HTML,而不是使用隐式值。

By the way, the documentation with the Scala version about all this has lots more information than the Java version. See here. It has more information on inputRadioGroup than the Java version of the documentation but still very useful reading for better understanding of all this.

顺便说一下,Scala版本的文档包含了比Java版本更多的信息。在这里看到的。它有更多关于inputRadioGroup的信息,而不是Java版本的文档,但仍然非常有用,以便更好地理解这些内容。

Some Play Bootstrap plugin has code available on GitHub that is also useful reading especially as it uses implicit values also.

一些Play Bootstrap插件在GitHub上有可用的代码,这对于阅读也很有用,尤其是当它使用隐式值时。

UPDATE

Here's a couple of GitHub projects showing how to achieve this:

以下是几个GitHub的项目,展示了如何实现这一点:

Screenshot of the result:

结果截图:

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

#2


1  

Move the implicitField-definition to the top of the file:

将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"))

This makes sure the implicit value is available where it's needed.

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

#3


1  

Always keep params on top of template and remove the following import statement @import helper.twitterBootstrap._ this will conflict with your own field constructor.

始终将params放在模板上,并删除下面的import语句@import helper.twitterBootstrap。这将与您自己的字段构造函数冲突。

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

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

Hope it resolves your issue.

希望它能解决你的问题。

#1


1  

The Twitter Bootstrap structure needs to be accurate.

Twitter的引导结构需要准确。

Wrap the btn-group class around the inputRadioGroup helper like so:

将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>  

Then replace the template with:

然后将模板替换为:

@(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>

In general, perhaps it'd be of interest another way of doing it. When you use fooForm("myField"...), you can use fooForm("myField[@i]"...) in a for loop where @i is a counter going from 0 to however many inputs there are. Then you can yourself sketch out the full HTML instead of using implicit values.

一般来说,也许用另一种方法来做会很有趣。当您使用fooForm(“myField”…)时,您可以在for循环中使用fooForm(“myField[@i]…”),其中@i是一个计数器,从0到任意输入。然后,您可以自己编写完整的HTML,而不是使用隐式值。

By the way, the documentation with the Scala version about all this has lots more information than the Java version. See here. It has more information on inputRadioGroup than the Java version of the documentation but still very useful reading for better understanding of all this.

顺便说一下,Scala版本的文档包含了比Java版本更多的信息。在这里看到的。它有更多关于inputRadioGroup的信息,而不是Java版本的文档,但仍然非常有用,以便更好地理解这些内容。

Some Play Bootstrap plugin has code available on GitHub that is also useful reading especially as it uses implicit values also.

一些Play Bootstrap插件在GitHub上有可用的代码,这对于阅读也很有用,尤其是当它使用隐式值时。

UPDATE

Here's a couple of GitHub projects showing how to achieve this:

以下是几个GitHub的项目,展示了如何实现这一点:

Screenshot of the result:

结果截图:

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

#2


1  

Move the implicitField-definition to the top of the file:

将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"))

This makes sure the implicit value is available where it's needed.

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

#3


1  

Always keep params on top of template and remove the following import statement @import helper.twitterBootstrap._ this will conflict with your own field constructor.

始终将params放在模板上,并删除下面的import语句@import helper.twitterBootstrap。这将与您自己的字段构造函数冲突。

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

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

Hope it resolves your issue.

希望它能解决你的问题。