在Grails中,出于演示目的,我们经常直接将域对象用作支持模型 ,并且仅在特殊情况下,我们才创建值持有者对象或DTO。
初级Grails开发人员知道如何在GSP中非常容易地显示各个实体属性,例如虚构的域类的名称……
<g:each in=”${breedingGoals}” var=”breedingGoal”>
${breedingGoal.name}
</g:each>
要么
<g:select from="${breedingGoals}"
optionKey="code"
optionValue="name" />
但是有时当新的展示要求出现时,却需要展示更多的或组合的信息时就会感到困难 。 人为设计特殊构造,例如:
def list() {
def breedingGoals = BreedingGoal.list()
breedingGoals.each {
it.name = it.name + " ("+ it.code + ")"
}
render view: “viewName”, model: [breedingGoals: breedingGoals]
}
不仅更多的显示名称,但还有其他方面-例如,它的代码。 那就是事情变得多毛的!
这似乎是在浏览器中获取新的正确输出的明智方法。 但事实并非如此。
在这种情况下,实际上会更新实体,并在几次迭代后将数据库置于非常混乱的状态。 在这一点上,经验丰富的开发人员告诉新手开发人员不要继续前进。
如果您是后者的一员,请先阅读GORM的一些持久性基础知识以获取一些背景知识 ,然后再按我个人的喜好返回此处获取可以使用的一些准则。
我的主要经验法则是:
不要用自身的(表示形式)替换数据。
这不是一个完整的列表,只是让您考虑一些选择,以我们前面提到的BreedingGoal用例为例。
如果信息包含在对象本身内
并且您有了对象,只需当场对其进行格式化。
<g:each in=”${breedingGoals}” var=”breedingGoal”>
${breedingGoal.name} (${breedingGoal.code})
</g:each>
GSP对于演示目的特别有用。 我总是会首先尝试。
有些人仍然对各种Grails标记感到困惑,这些标记似乎接受要迭代的域类的“仅1个属性”,例如select 。
<g:select from="${breedingGoals}"
optionKey="code"
optionValue="name + (code)? Arh! Now what?" />
幸运的是,有时标签作者对此有所考虑! 对于select, optionValue
可以使用闭包来应用转换:
<g:select from="${breedingGoals}"
optionKey="code"
optionValue="${{ it.name + ' (' + it.code + ')' }}" />
如果更多地方需要此新格式的信息
要保持干燥,您可以将其放入对象本身。 在您的域类中,添加一个getter :
String getNamePresentation() {
name + " ("+ code + ")"
}
可以将其称为对象本身的属性。
<g:each in=”${breedingGoals}” var=”breedingGoal”>
${breedingGoal.namePresentation}
</g:each>
如果信息不在对象本身中,但是需要使用其他逻辑进行检索/计算
您可以使用标签库 。
class BreedingGoalTagLib {
def translationService
def formatBreedingGoal = { attrs ->
out << translationService.translate(attrs.code, user.lang)
}
}
在此示例中,某种translationService
用于获取要显示的实际信息。
<g:each in=”${breedingGoals}” var=”breedingGoal”>
<g:formatBreedingGoal code=${breedingGoal.code} />
</g:each>
从GSP调用TagLib非常容易,并且可以完全访问协作服务和Grails环境。
如果不需要其他计算,只需要特殊(HTML)格式
获取内容并将其委托给模板:
class BreedingGoalTagLib {
def displayBreedingGoal = { attrs ->
out << render(template: '/layouts/breedingGoal',
bean: attrs.breedingGoal)
}
}
特定模板可能包含一些用于特殊标记HTML:
<strong>${it.name}</strong> (${it.code})
或者您需要一次初始化一堆东西,或者您并不是真正针对GSP显示
您可以创建一个(瞬态)属性,并在初始化时填充它。
调整域类,使新的“ presentation”属性处于瞬态状态 (这样它就不会持久化到数据库中)并在某个时候对其进行初始化。
class BreedingGoal {
String code
String name
String namePresentation
static transients = ['namePresentation']
}
class SomeInitializationService {
// in some method...
def breedingGoal = findBreedingGoal(...
breedingGoal.namePresentation =
translationService.translate(breedingGoal.code, user.lang)
return breedingGoal
}
友善的建议: 不要为了过渡目的而用过渡字段过多地填充域类。 如果您的域类包含50%带有奇怪代码的属性,而您需要另一半具有关联的“表示”字段,则最好使用专门的值或DTO对象。
结论
您会看到各种各样的演示选项,并且取决于信息的来源和去向,某些方法比其他方法更适合。 不要害怕在必要时选择一种方法并重构为另一种方法,但是我会坚持使用GSP中最简单的选项,并尽量不要为了演示目的而过多地处理核心数据!