【问题标题】:Grails layoutResources equivalent for asset-pipeline资产管道的 Grails layoutResources 等效项
【发布时间】:2013-11-16 16:33:27
【问题描述】:

我正在尝试使用asset-pipeline plug-in (v1.0.4) 在 grails v2.3.1 中设置 SiteMesh 布局,但我不确定如何处理在模板的特定位置包含 javascript 资源(例如如果使用resources plugin,您将使用 r:layoutResources。

示例布局(grails-app/views/layouts/test.gsp):

<html>
   <head>
      <title><g:layoutTitle/></title>
      <g:layoutHead/>
   </head>
   <body>
      <div class="thecontent">
         <g:layoutBody/>
      </div>
      <asset:javascript src="application.js"/>
      <!-- WANT DECORATED PAGE RESOURCES TO BE INCLUDED HERE -->
   </body>
</html>

示例 gsp (grails-app/views/test.gsp):

<html>
   <head>
      <meta name="layout" content="test"/>
      <title>The Title</title>
      <asset:stylesheet src="thispageonly.css"/>
   </head>
   <body>
      <div id="helloworld">
         Hello World
      </div>
      <asset:javascript src="thispageonly.js"/>
   </body>
</html>

生成的装饰页面(忽略资产管道捆绑/等)适用于样式表(因为它在头部)但对于 javascript 失败:

<html>
   <head>
      <meta name="layout" content="test"/>
      <title>The Title</title>
      <link rel="stylesheet" href="/assets/thispageonly.css?compile=false"/>
   </head>
   <body>
      <div class="thecontent">
         <div id="helloworld">
            Hello World
         </div>
         <!-- *** NOT WHERE I WANT THIS *** -->
         <script src="/assets/thispageonly.js?compile=false" type="text/javascript"></script>
      </div>
      <script src="/assets/application.js?compile=false" type="text/javascript"></script>
   </body>
</html>

目前我可以让它工作的唯一方法是使用 g:applyLayout 和 g:pageProperty:

<!-- grails-app/views/layouts/test2.gsp -->
<html>
   <head>
      <title><g:layoutTitle/></title>
      <g:layoutHead/>
   </head>
   <body>
      <div class="thecontent">
         <g:layoutBody/>
      </div>
      <asset:javascript src="application.js"/>
      <g:pageProperty name="page.javascript"/>
   </body>
</html>


<!-- grails-app/views/test2.gsp -->
<g:applyLayout name="test2">
<html>
   <head>
      <title>The Title</title>
      <asset:stylesheet src="thispageonly.css"/>
   </head>
   <body>
      <div id="helloworld">
         Hello World
      </div>
      <content tag="javascript">
         <asset:javascript src="thispageonly.js"/>
      </content>
   </body>
</html>
</g:applyLayout>

但是这种与元标记的背离似乎过于复杂(另外我不清楚记录不充分的 g:pageProperty 是否会在未来的升级中得到支持)。最好的长期方法是什么?

【问题讨论】:

  • 使用资源标签和
  • @john,我现在想坚持使用资产管道插件...对于a number of reasons

标签: grails resources asset-pipeline


【解决方案1】:

不确定您的要求。 在我的项目中,我需要将所有 JavaScript 文件移动到页脚。 我将“布局资源”标签从页眉移到页脚,效果很好。

<footer class="clear">
    <r:layoutResources>
</footer>

【讨论】:

    【解决方案2】:

    仅供参考:这对我来说效果很好,虽然有点难看。

    <!-- grails-app/views/layouts/test2.gsp -->
    <html>
       <head>
          <title><g:layoutTitle/></title>
          <g:layoutHead/>
       </head>
       <body>
          <div class="thecontent">
             <g:layoutBody/>
          </div>
          <asset:javascript src="application.js"/>
          <asset:deferredScripts/>
       </body>
    </html>
    
    <!-- grails-app/views/test2.gsp -->
    <g:applyLayout name="test2">
    <html>
       <head>
          <title>The Title</title>
          <asset:stylesheet src="thispageonly.css"/>
       </head>
       <body>
          <div id="helloworld">
             Hello World
          </div>
          <asset:script src="${assetPath(src: 'thispageonly.js')}" type="text/javascript" />
       </body>
    </html>
    </g:applyLayout>
    

    【讨论】:

      【解决方案3】:

      忽略此答案,请参阅下面asset-pipeline 创建者的评论,了解它是如何以&lt;asset:script&gt; 形式存在于资产管道插件中的。

      旧答案:

      Sitemesh 让我头疼,据我所知,使用 g:layoutHeadg:layoutBody 标签的解决方案并不是让你收集一堆不同的 javascript sn-ps 并将它们全部发出在一个块中。

      我最近开始开发的应用程序大量利用了r:script 标签,并且会在单个 gsp 页面中多次使用它(在各种包含的模板上)。

      我打算如何在我的应用程序中解决这个问题是使用一个标签库,它几乎可以完成r:script 标签所做的工作。收集所有延迟的 javascript sn-ps 并在用户要求发出的地方发出它。

      这是一个可以执行此操作的示例 taglib:

      class DeferTagLib {
          static namespace = 'defer'
          static defaultEncodeAs = [emitScript: 'raw']  // we actually want to inject javascript :)
      
          private static JAVASCRIPT_DEFER_PROPERTY = "__defer__javascript"
      
          protected String removeJavascript() {
              try {
                  return getJavascript()
              } finally {
                  getRequest().removeAttribute(JAVASCRIPT_DEFER_PROPERTY)
              }
          }
      
          protected String getJavascript() {
              return getRequest().getAttribute(JAVASCRIPT_DEFER_PROPERTY)
          }
      
          protected void appendJavascript(String javascript) {
              String currentValue = getJavascript() ?: ""
              getRequest().setAttribute(JAVASCRIPT_DEFER_PROPERTY, currentValue + javascript)
          }
      
          // For gathering script tags in your gsp files and emitting them in the footer of the layout
          // A replacement for the r:script tag that came with Resources, but is not in the asset-pipeline plugin
          def script = { attrs, body ->
              appendJavascript(body())
          }
      
          // Used in the layout to emit all the script that has been deferred so far in the footer of the page
          // A replacement for the <r:layoutResources/> tag that emitted javascript in the footer
          // (the 2nd time it was called, kind of confusing...)
          def emitScript = { attrs ->
              out << "<script>${removeJavascript()}</script>"
          }
      }
      

      然后,在我的 gsp 页面/片段中,我可以将之前对 r:script 的任何调用替换为 defer:script

      <defer:script>
          // this is before
      </defer:script>
      
      ... maybe another template that's included ...
      
      <defer:script>
          alert("foo!");
      </defer:script>
      
      ...  later
      
      <defer:script>
          // after
      </defer:script>
      

      在我的布局中,在底部我可以这样做

      <html>
        <head>
           .... shared head stuff ....
          <g:layoutHead/>
        </head>
      
        <body>
          .... body stuff before layout
          <g:layoutBody/>
          .... other body stuff after layout
          <defer:emitScript/>
        </body>
      </html>
      

      这最终会在看起来像的主体之后创建一个部分

          ... the actual body from the gsp
      
          <script>
            // this is before ...
            alert("foo!");
            // after
          </script>
        </body>
      </html>
      

      【讨论】:

      • 自从最初编写这个堆栈溢出问题以来,asset-pipeline 已经实现了类似的延迟。请查看有关延迟脚本的文档:bertramdev.github.io/asset-pipeline/guide/…
      • 然后渲染输出:
      【解决方案4】:

      使用站点网格来控制对象放置完全是一个有效的用例。 Sitemesh 不会去任何地方,我实际上用它来控制子容器和布局继承。在资产管道插件中复制这个内置的流控制是没有意义的。

      另一个非常有用的技巧是使用&lt;g:layoutHead/&gt;&lt;g:layoutBody/&gt; 标签,它们也非常有用。

      如果这被证明是一个问题,将来我们可能会考虑提供一种具有这种行为的模式。让我知道资产管道的进展情况,如果出现此类问题,请随时在 github 上开票。显然,我还需要注意堆栈溢出 :)

      【讨论】:

      • 感谢@davydotcom;验证这个想法是件好事。到目前为止,我们对该插件印象深刻。拥有更多关于资源插件项(如g:layoutBodyg:javascript)的关系(或替换?)的文档会很有帮助。此外,如果您尝试完全使用资产管道插件,built-in grails ajax remote functions 似乎不可用;所以一些关于这方面的文档也会有所帮助。
      • 我能够让 remoteLink ajax 东西在没有资源插件的情况下完美工作。你能不能给我举个例子?
      • 也许我误解了如何配置项目,但这里有一个快速复制的方法:
      • 1) create-app remoting, 2) 编辑 BuildConfig 以添加 compile ":asset-pipeline:1.5.1" 并注释掉 runtime ":jquery:1.10.2.2"runtime ":resources:1.2.1", 3) create-controller test, 4) 添加控制器方法:def helloworld() { render "Hello world!" } , 5) 用&lt;g:javascript library="jquery"/&gt;&lt;asset:javascript src="deps_on_jquery.js"/&gt; 在标头加上&lt;g:remoteLink controller="test" action="hello world" update="mainContent"&gt;Say what?&lt;/g:remoteLink&gt; 在正文中创建index.gsp。
      • 以上场景会抛出:Class org.codehaus.groovy.grails.web.taglib.exceptions.GrailsTagException Message: No javascript provider is configured
      猜你喜欢
      • 2016-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-16
      • 2015-03-21
      相关资源
      最近更新 更多