Загрузка статических ресурсов из пользовательского тега Grails с расположением: 'head'

Я пытаюсь написать собственный тег Grails, который (среди прочего) запускает включение ресурса, поэтому что-то вроде <myTags:view name="foo"/> будет загружать, скажем, js/views/foo.js. И я хочу, чтобы он загружался с disposition: 'head'.

Я мог бы использовать <r:external/>, но это не поместило бы его в <head>, это просто создало бы встроенный тег <script/>. И я мог бы использовать <r.script/>, но это не позволяет мне ссылаться на путь; Мне нужно, чтобы мой пользовательский тег считывал файл и выгружал его в out.

Теперь, если бы foo.js был отдельным модулем, я мог бы сделать что-то вроде: r.require([module: 'foo']), но это не так; часть смысла в том, что я не хочу объявлять все эти файлы в ApplicationResources.groovy. Но, может быть, я мог бы ApplicationResources.groovy создать модули программно, прочитав доступные файлы - это возможно? Или есть лучший способ?


person David Moles    schedule 16.01.2013    source источник
comment
На самом деле смысл в том, чтобы создавать модули и просто включать их на те страницы, которые в них нуждаются. Я вижу, что вы пытаетесь сделать, однако я не думаю, что это сработает на практике. Потенциально вы можете загрузить этот файл более одного раза, если используете тег более одного раза. Вам будет лучше, если вы будете следовать соглашениям Grails.   -  person James Kleeh    schedule 17.01.2013
comment
По опыту я знаю, что если я попрошу разработчиков помнить каждый раз, когда они создают представление, чтобы создать запись модуля в ApplicationResources.groovy, и каждый раз, когда они используют представление, чтобы убедиться, что есть тег ‹r:require/›, они будут забыть, и мы будем сталкиваться с одними и теми же проблемами снова и снова. Я бы предпочел не нарушать DRY.   -  person David Moles    schedule 17.01.2013
comment
Хм, похоже, что на самом деле ‹r:external/› должен поместить это в голову, но, насколько я могу судить, он ничего не делает.   -  person David Moles    schedule 18.01.2013


Ответы (1)


В итоге я пошел к тому, чтобы 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'

        }
    }
}

Затем taglib:

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>

Это не красиво, но беспорядок в основном скрыт, и это должно значительно сократить шаблоны и возможности забыть добавить волшебные строки в несколько файлов.

person David Moles    schedule 17.01.2013