我最终朝着以ApplicationResources.groovy
编程方式创建模块的方向前进,因此自定义标签可以使用<r:require/>
.
这个想法是,对于每个 Backbone 视图,在 下web-app/myApp/views
,文件中有一个 Backbone 视图,.js
文件中有一个 Handlebars 模板.handlebars
(按照惯例,具有相同的名称)。该.handlebars
文件被声明为普通模块,但由 Handlebars-Resources 插件预编译。
中的一些代码ApplicationResources.groovy
查找所有视图并创建相应的资源模块:
GrailsApplication grailsApplication = Holders.getGrailsApplication()
File viewsDir = grailsApplication.parentContext.getResource("myApp/views").file;
if (viewsDir.exists() && viewsDir.isDirectory() && viewsDir.canRead()) {
String[] viewsJS = viewsDir.list().findAll { name ->
name.endsWith("View.js")
}
String[] views = viewsJS.collect { name ->
name.substring(0, name.length() - ".js".length())
}
for (view in views) {
"${view}" {
dependsOn 'backbone', 'backbone_relational', 'handlebars'
resource url: "dpg/views/${view}.handlebars",
attrs: [type: 'js'],
disposition: 'head'
resource url: "dpg/views/${view}.js",
disposition: 'head'
}
}
}
然后是标签库:
class ViewsTagLib {
static namespace = "myApp"
def view = { attrs ->
r.require(module: "${attrs.name}View")
out << "<${attrs.tagName} id='${attrs.id}'></${attrs.tagName}>"
}
}
在 GSP 中调用它:
<myApp:view tagName="div" name="foo" id="foo1"/>
<myApp:view tagName="div" name="foo" id="foo2"/>
产生:
<html>
<head>
...
<!--
Automagically generated modules (included only once).
Should put these in the same bundle, but handlebars-resources
gets confused.
-->
<script src="/myApp/static/bundle-bundle_fooView_handlebars.js"
type="text/javascript" ></script>
<script src="/myApp/static/bundle-bundle_fooView_head.js"
type="text/javascript" ></script>
</head>
<body>
...
<div id="foo1"></div> <!-- backbone placeholder for 1st view instance-->
<div id="foo2"></div> <!-- backbone placeholder for 2nd view instance-->
</body>
</html>
它并不漂亮,但混乱大部分是隐藏的,它应该大大减少样板文件和忘记向多个文件添加魔术字符串的机会。