Node.js с Express: импорт javascript на стороне клиента с использованием тегов script в представлениях Jade?

У меня есть экспресс-сервер node.js, работающий с механизмом шаблонов Jade.

У меня есть нефритовый файл макета, который импортирует тело отдельных представлений, например:

!!!
html

    head
        title= title || 'Title not set.'

    body
        #header
            h1 Header.

        #content!= body //- this renders the body of an individual view

        #footer
            p Footer.

Например, следующая индексная страница:

p Welcome to the front page.

p This page serves as a now.js test.

Это прекрасно работает. Однако теперь я хочу включить две библиотеки javascript на стороне клиента специально для этой страницы индекса (и, следовательно, не очень для каждой страницы, поэтому я не могу поместить ее в заголовок макета).

Это работает:

//- import jquery
script(type='text/javascript', src='./jquery-1.5.2.min.js');

//- import now.js (hosts itself)
script(type='text/javascript', src='/nowjs/now.js')

//- import the chat client
script(type='text/javascript', src='./indexChatClient.js')

p Welcome to the front page.

p This page serves as a now.js test.

Однако это загружает скрипты в тело полной страницы, что не является допустимым HTML, верно?

Насколько я знаю, скрипты должны быть загружены в голову, если я хочу сделать это правильно, но раздел головы обрабатывается файлом макета.

Итак, как мне правильно включить эти библиотеки javascript на стороне клиента специально для определенного представления/страницы?


person Tom    schedule 09.04.2011    source источник


Ответы (6)


Я сделал то же самое, используя решение из этой темы:

http://groups.google.com/group/express-js/browse_thread/thread/8c2006dc7bab37b1/f9a273c836e0a2ac

Вы можете объявить переменную «скрипты» в параметрах просмотра:

приложение.js:

app.set('view options', { locals: { scripts: ['jquery.js'] } });  // You can declare the scripts that you will need to render in EVERY page

Затем у вас может быть помощник, который отображает теги сценария в заголовке макета.

Вспомогательный код renderScriptTags():

app.helpers({ renderScriptTags: function(scripts) {
  return scripts.map(function(script) {
    return '<script src="scripts/' + script + '"></script>';
  }).join('\n ');

В шаблон макета в разделе head у вас будет:

- renderScriptTags(scripts)

Теперь, чтобы добавить скрипт в тег head, вам просто нужно вставить скрипт в переменную «scripts» в вашем шаблоне содержимого jade (шаблоне body):

- scripts.push('myscript.js'); 

Таким образом, страница будет отображать jquery.js и myscript.js в заголовке страницы.

ОБНОВЛЕНИЕ

Кажется, что новейшая экспресс-версия обрабатывает местных жителей по-другому, чтобы все работало правильно, вы можете сделать это (хотя я не уверен, что это оптимальное решение, мне нужно немного покопаться)

Вы можете использовать помощник renderScriptTags() предыдущего метода в своем шаблоне макета, как и раньше.

Но не устанавливайте переменные скриптов в локальные, вместо этого создайте динамический помощник, который сделает переменную scripts доступной в наших шаблонах:

app.dynamicHelpers({
  scripts: function(req, res){
    return ['jquery.js']; //this will be available in all views
  }
});

А затем, чтобы добавить конкретный скрипт из вашего шаблона тела (точно так же, как и раньше):

- scripts.push('myscript.js'); 

Теперь для этого конкретного представления у вас должны быть правильно отображены jquery.js и myscript.js.

person BFil    schedule 09.04.2011
comment
Вы просто добавляете их в список общих сценариев, которые затем добавляются в заголовок в файле общего макета. Однако мне нужно установить определенные сценарии для определенных представлений. - person Tom; 09.04.2011
comment
Вы можете установить сценарий для представления в представление, вставив его в переменную scripts, чтобы он отображался в заголовке макета, разве это не именно то, что вы пытаетесь выполнить? - person BFil; 10.04.2011
comment
@ShadowCloud, я не уверен, куда вы нажимаете на скрипты var? Разве это не приводит к тому, что скрипт загружается в других представлениях, а скрипты var кажутся объявленными в основной области видимости? - person Tom; 10.04.2011
comment
@Tom: я заметил, что локальные жители обрабатываются по-другому в более новых версиях экспресса, я предоставил вам измененные решения в моем обновленном ответе (возможно, есть решения получше, но они должны работать) - person BFil; 11.04.2011
comment
@ShadowCloud, где вы вызываете script.push? В конкретном представлении? - person Tom; 11.04.2011
comment
@Tom: да, в твоих шаблонах контента - person BFil; 11.04.2011
comment
@ShadowCloud, разве это не меняет массив скриптов, поэтому этот скрипт также не включается в предстоящие представления? - person Tom; 11.04.2011
comment
@Tom: нет, переменная scripts изменяется только в этом запросе (для этого предназначался local). dynamicHelper прикрепляет переменную scripts к каждому входящему запросу - person BFil; 11.04.2011
comment
@ShadowCloud, скрипты — это функция, а не массив, поэтому вы не можете использовать push и т. д. Это не работает. - person Tom; 17.04.2011
comment
@ShadowCloud Я пытаюсь использовать вашу функцию renderScriptsTags в своем макете. Я называю это в разделе заголовка, ссылаясь на «- renderScriptTags(scripts)». Я проверил в отладчике, и ваша функция правильно генерирует теги скрипта, но они не помещаются в html, сгенерированный jade. Изменилось ли что-то в работе хелперов и нефрита с тех пор, как вы ответили на этот вопрос? Спасибо! - person Evan; 11.08.2011
comment
Я не уверен, мне кажется, что он не изменился, хотя вы можете сами проверить журналы изменений: github.com/visionmedia/express/blob/master/History.md и github.com/visionmedia/jade/blob/master/History.md - person BFil; 11.08.2011
comment
Должен ли вызов - renderScriptTags(scripts) быть != renderScriptTags(scripts)? Я не мог вывести теги скрипта только при использовании -. - person Tim Banks; 11.03.2012

Вы можете иметь их на макете и указать, какие библиотеки загружать на «контроллеры».

// layout.jade
!!!
html

    head
        title= title || 'Title not set.'
        -each script in scripts 
          script(type='text/javascript', src= script)
    body
        #header
            h1 Header.

        #content!= body //- this renders the body of an individual view

        #footer
            p Footer.

И ваш "контроллер":

// app.js
app.get('/', function (req, res) {
  res.render({
    scripts: ['jquery.min.js', '/nowjs/now.js']
  }
}
person masylum    schedule 10.04.2011
comment
Я думаю, это лучше, так как скрипты должны быть установлены не в представлении, а в контроллере. - person Tom; 11.04.2011
comment
Я предпочитаю иметь их на контроллерах, чтобы вы могли легко повторно использовать этот код. - person masylum; 12.04.2011
comment
как вы устанавливаете скрипт, который включен везде, без явной настройки в отдельном рендере/представлении? - person Tom; 16.04.2011
comment
Чтобы сценарий отображался везде в Express 3.0 без явной настройки его в отдельном представлении, вы можете использовать app.locals, то есть: app.locals.globalScripts = ['/lib/jquery-min.js', '/lib/bootstrap. мин.js']; и в представлении: -каждый сценарий в сценарии globalScripts (type = 'text/javascript', src = script) в соответствии с ответом masylum выше. - person hillmark; 29.12.2012
comment
Но разве это не загружает все сценарии во всех представлениях? Я думал, что ОП хочет загружаться только для определенного вида? - person CocoHot; 03.11.2014

Это можно сделать правильным способом (tm) в последней версии Jade (0.28.1), сохранив все это внутри шаблонов/представлений, не взламывая содержимое страницы (ссылки на скрипты) в другом месте:

  • Объявите голову как именованный блок в вашем шаблоне:
doctype 5
html
  head
    // named block allows us to append custom head entries in each page
    block head
        title= title
        link( rel='stylesheet', href='/css/style.css' )
        script( type="text/javascript", src="/js/some-default-script.js" )
  body
    block content
  • Добавьте свои элементы заголовка для конкретной страницы (включая теги скрипта) в свои представления:
extends layout

// here we reference the template head and append to it
block append head
    meta( name="something", content="blah" )
    link( href="/css/another.css", rel="stylesheet", type="text/css" )
    style
        div.foo {
            position: absolute;
        }
    script( src="/js/page-specific-script.js" )

block content
    #page-contents-follows
person Gilead    schedule 17.01.2013

Я предполагаю, что проблема (из краткого чтения этого) заключается в том, что вы не «сбрасываете» массив, устанавливая его .length в 0, чтобы удалить старые значения, поэтому каждый запрос может просто нажимать все больше и больше строк

person tjholowaychuk    schedule 17.04.2011
comment
но мне не нужно делать это правильно? Я думал, что локальные жители устанавливались каждый раз при отображении представления? Кроме того, не является ли этот способ добавления сценариев очень неэффективным, поскольку каждый запрос в основном имеет одни и те же сценарии, поэтому его следует кэшировать как фиксированный html, а не каждый раз повторять массив? - person Tom; 17.04.2011

Вот альтернативный способ сделать это (используя ответ ShadowCloud). Немного обобщив, вы можете указать как локальные, так и удаленные сценарии, а затем отложить их до загрузки страницы:

приложение.js:

app.dynamicHelpers({
    scripts: function() {
        //scripts to load on every page
        return ['js/jquery.min.js','js/jquery-ui.min.js','js/all.js'];
    }
});

затем вы можете добавить локальные или удаленные сценарии в любой точке представления.

//- local script
- scripts.push('js/myPage.js');
//- remote script ( note: this is a schemeless url. You can use http(s)? ones too )
- scripts.push('//platform.twitter.com/widgets.js')

layout.jade: (я поместил его в конец body, чтобы сначала загрузить видимые вещи, но на самом деле он может идти куда угодно)

//- Bring the scripts into a client-side array,
//-    and attach them to the DOM one by one on page load
script
    var page_scripts = !{'["' + scripts.join('","') + '"]'};
    function loadJS() {
        for(var i in page_scripts) {
            var e = document.createElement("script");
            e.src = page_scripts[i];
            document.body.appendChild(e);
        }
    }
    // Check for browser support of event handling capability
    if (window.addEventListener)
        window.addEventListener("load", loadJS, false);
    else if (window.attachEvent)
        window.attachEvent("onload", loadJS);
    else window.onload = loadJS;
person jhoff    schedule 27.11.2011

Я не уверен, в чем смысл подходов до сих пор. Для меня гораздо чище сделать следующее...

layout.jade:

doctype html
html
  head
    title= title
    block headscripts  // placeholder for scripts that need to be in the <head>
    link(rel='stylesheet', href='/styles/style.css')
    block styles       // placeholder for styles
  body
    block content
    script(src='/libs/jquery/dist/jquery.min.js') // this will render before all scripts in every page
    block scripts  // placeholder for scripts that go in the body

какая-то страница.jade:

extends layout

block styles  // this will render in the <head>
  link(rel='stylesheet', href='/styles/films.css')
  link(rel='stylesheet', href='/styles/pagination.css')

block headscripts  // this will also render in the <head>
  script(src='/js/somescript.js')

block content
  h1= title
  div.someotherstuff

block scripts  // this will render at the end of the body
  script(src='/js/libs/someotherscript.js')
  scirpt(src='/libs/doT/doT.js')

Таким образом, не имеет значения, где на ваших .jade страницах вы поместите свои блоки, они всегда будут отображаться в правильных местах.

person Stephen Collins    schedule 04.04.2014