17

I added the resources plug-in in a grails 1.3.7 application and everything works fine except javascript that is loaded asynchronously.

So if I have a template that contains a

<r:script>
    // javascript here
</r:script>

and load it via ajax the js code does not execute, and I get this error:

It looks like you are missing some calls to the r:layoutResources tag

which makes sense because the page has already been rendered and there is no r:layoutResources to handle the newly added r:script js code.

The only workaround I've found is to add render r.layoutResources(disposition:"defer") after the actual render(template:...) in the controller actions that render content asynchronously.

Is there any other more clear solution?

sikrip
  • 671
  • 8
  • 10

3 Answers3

18

A better approach would be to have a dedicated layout for your AJAX responses:

<g:layoutBody/>
<r:layoutResources disposition="defer"/>

If you're using Grails 2.0, you can specify the layout in the render method (render template: "...", layout: "ajax"). Otherwise, use layout by convention.

Peter Ledbrook
  • 4,302
  • 1
  • 21
  • 18
  • Very useful information! I had already looked for something like this in the [docs](http://grails.org/doc/latest/ref/Tags/render.html), but the render tag has no "layout" attribute there as of 2.0.3. – Ingo Kegel May 22 '12 at 14:26
  • I think this is the best approach. – sikrip Jun 12 '13 at 15:01
  • Nice solution, still throwing the error but in the HTML source is printing the required modules. Now the problem is to eval that script tags because they still doesn't work. Even passing the script tags to eval function. – IgniteCoders Aug 20 '14 at 12:39
  • I solved it. take a look to [this](http://stackoverflow.com/a/25406725/2835520) answer if your added javascript doesn't work. – IgniteCoders Aug 20 '14 at 14:11
4

A better option I think is to not use r:script in your template fragment. Just use normal script tag. You are not getting any benefit from Resources inside these fragments if you don't need the layoutResources stuff.

Sometimes the classic way is the best.

Marc Palmer
  • 507
  • 2
  • 9
  • 2
    If I use the classic script tag, in the initial page load the js code won't execute because it depends on libraries (jQuery etc) that are loaded at the bottom of the page (and I don't want to load them in the head). – sikrip Jan 06 '12 at 11:38
  • 1
    jQuery should normally be in head disposition and execute first anyway. All your solutions here are going to be pretty hacky because you are effectively returning dynamic script fragments, which is not what resources is designed for. It seems this approach is causing you some problems, but if you wish to continue with it I would suggest you don't use Resources tags in the main GSP that is first served. – Marc Palmer Jan 06 '12 at 11:50
1

I always go with Peter Ledbrook response, but instead of using a layout, I use a template and I automate what to render in the main layout. My main.gsp looks like the following:

<!DOCTYPE html>
<g:if test="${request.xhr}">
    <g:render template="/layouts/content" />
</g:if>
<g:else>
    <html>
   ...  <!-- Main layout stuff: application resources, logo, main menu, etc -->
   <div id="content">  <!-- Somewhere in the body -->
          <g:render template="/layouts/content" />
       </div>
    </html>
</g:else>

Then, the _content.gsp template looks like:

<g:layoutBody />
<r:layoutResources disposition="defer"/>
<!-- Ajaxify your relative links with the framework of your choice -->

That way, I can automatically ajaxify all relative links and forms and no action is required in the controller (no different responses), since the ajax response always goes inside the content div.

Deigote
  • 1,721
  • 14
  • 15
  • BTW I usually go with my own ajaxify function to ajaxify all relative links... Just in case someone is interested: https://github.com/deigote/ajaxify/ – Deigote May 03 '13 at 08:42