Reputation: 8127
I've got a node.js express server running with the Jade template engine.
I've got a layout jade file which imports the body of individual views like so:
!!!
html
head
title= title || 'Title not set.'
body
#header
h1 Header.
#content!= body //- this renders the body of an individual view
#footer
p Footer.
For example, the following index page:
p Welcome to the front page.
p This page serves as a now.js test.
This works fine. However, I now want to include two client-side javascript libraries specifically for this index page (and thus not very every page, which is why I cannot put it in the head of layout).
This works:
//- 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.
However, this loads the scripts to the body of the complete page, which is not valid HTML, right?
As far as I know, the scripts should be loaded into the head if I want to do it properly, but the head section is handled by the layout file.
So, how would I properly include these client-side javascript libraries specifically for a certain view/page?
Upvotes: 21
Views: 52476
Reputation: 3653
I'm not sure what the point of the approaches so far are. To me it is much cleaner to do the following...
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
somepage.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')
This way it doesn't matter where in your .jade
pages you put your blocks, they will always render in the correct places.
Upvotes: 1
Reputation: 1526
It's possible to do it The Right Way (tm) in the latest Jade (0.28.1) by keeping it all inside templates/views, without hacking page contents (script links) elsewhere:
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
Upvotes: 8
Reputation: 716
Here is an alternate way to do it ( using ShadowCloud's answer ). By generalizing a bit, you can specify both local and remote scripts, and then defer them until after page load:
app.js:
app.dynamicHelpers({
scripts: function() {
//scripts to load on every page
return ['js/jquery.min.js','js/jquery-ui.min.js','js/all.js'];
}
});
then, you can add local or remote scripts at any point inside a view
//- 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: ( I put it at the end of body to load the visible stuff first, but it can go anywhere really )
//- 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;
Upvotes: 2
Reputation: 3076
I assume the problem (from briefly reading this) is that you are not "flushing" the array, setting its .length to 0 to remove old values, so each request could just be pushing more and more strings
Upvotes: 2
Reputation: 13096
I've done the same using the solution from this thread:
http://groups.google.com/group/express-js/browse_thread/thread/8c2006dc7bab37b1/f9a273c836e0a2ac
You can declare a "scripts" variable into view options:
app.js:
app.set('view options', { locals: { scripts: ['jquery.js'] } }); // You can declare the scripts that you will need to render in EVERY page
Than you can have an helper that renders the script tags into the head of the layout
renderScriptTags() Helper code:
app.helpers({ renderScriptTags: function(scripts) {
return scripts.map(function(script) {
return '<script src="scripts/' + script + '"></script>';
}).join('\n ');
Into the layout template in the head section you will have:
- renderScriptTags(scripts)
Now, to add a script on the head tag, you'll just need to push the script into the "scripts" variable on your jade content template (body template):
- scripts.push('myscript.js');
In this way the page will render jquery.js and myscript.js into the head of the page
UPDATE
It seems that newest express version handles the locals in a different way, to make this work properly, you can do this (I'm not sure it is the optimal solution though, I'd need to dig this a little)
You can use the renderScriptTags() helper of the previous method in your layout template like before.
But don't set the scripts variables into locals, instead create a dynamic helper that will make a scripts variable available in our templates:
app.dynamicHelpers({
scripts: function(req, res){
return ['jquery.js']; //this will be available in all views
}
});
And then, to add a specific script, from your body template (exactly as before):
- scripts.push('myscript.js');
Now, for this specific view, you should have jquery.js and myscript.js rendered properly
Upvotes: 20
Reputation: 22371
You can have them on the layout and specify which libraries to load on the "controllers".
// 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.
And your "controller":
// app.js
app.get('/', function (req, res) {
res.render({
scripts: ['jquery.min.js', '/nowjs/now.js']
}
}
Upvotes: 40