This example will show you how to compile templates into nested namespaces that correspond to the directories where the templates live.
gulp-declare
- Declare properties and sub-propertiesgulp-concat
- Combine output into a single filegulp-wrap
- Addrequire()
andHandlebars.template()
statements
This example assumes a directory structure that looks something like this:
├── gulpfile.js # Your gulpfile
└── source/ # Your application's source files
└── templates/ # A folder containing sub-folders and templates named with dot notation
├── App.hbs
└── App/
| ├── header.hbs
| ├── footer.hbs
| └── etc.hbs
└── Other.item.hbs
The template files will be compiled to the following namespace paths:
File path | Namespace path |
---|---|
source/templates/App.hbs | MyApp.templates.App |
source/templates/App/header.hbs | MyApp.templates.App.header |
source/templates/App/footer.hbs | MyApp.templates.App.footer |
source/templates/Other.item.hbs | MyApp.templates.Other.item |
Note: You can have multiple levels of directories under source/templates/
for deeper nesting and can still use dot notation within the filename to nest further if you like.
Note: MyApp.templates.App.header
is a function that is stored as a property of the MyApp.templates.App
function. As everything in JavaScript is an object, even functions, this is perfectly valid and works in all environments. If this hurts your brain, store source/templates/App.hbs
as source/templates/App/main.hbs
and access it as MyApp.templates.App.main
.
Type the following commands from the root of this repository:
npm install
cd examples/namespaceByDirectory
gulp
cat build/js/templates.js
You should see the following output:
this["MyApp"] = this["MyApp"] || {};
this["MyApp"]["templates"] = this["MyApp"]["templates"] || {};
this["MyApp"]["templates"]["App"] = Handlebars.template(/* compiled template */);
this["MyApp"]["templates"]["Other"] = this["MyApp"]["templates"]["Other"] || {};
this["MyApp"]["templates"]["Other"]["item"] = Handlebars.template(/* compiled template */);
this["MyApp"]["templates"]["App"] = this["MyApp"]["templates"]["App"] || {};
this["MyApp"]["templates"]["App"]["footer"] = Handlebars.template(/* compiled template */);
this["MyApp"]["templates"]["App"]["header"] = Handlebars.template(/* compiled template */);
Follow these steps to use this approach in your project.
npm install --save-dev gulp-handlebars gulp-wrap gulp-declare gulp-concat
var gulp = require('gulp');
var wrap = require('gulp-wrap');
var concat = require('gulp-concat');
var declare = require('gulp-declare');
var handlebars = require('gulp-handlebars');
gulp.task('templates', function() {
// Load templates from the client/templates/ folder relative to where gulp was executed
gulp.src('client/templates/**/*.hbs')
// Compile each Handlebars template source file to a template function
.pipe(handlebars())
// Wrap each template function in a call to Handlebars.template
.pipe(wrap('Handlebars.template(<%= contents %>)'))
// Declare template functions as properties and sub-properties of MyApp.templates
.pipe(declare({
namespace: 'MyApp.templates',
noRedeclare: true, // Avoid duplicate declarations
processName: function(filePath) {
// Allow nesting based on path using gulp-declare's processNameByPath()
// You can remove this option completely if you aren't using nested folders
// Drop the client/templates/ folder from the namespace path by removing it from the filePath
return declare.processNameByPath(filePath.replace('client/templates/', ''));
}
}))
// Concatenate down to a single file
.pipe(concat('templates.js'))
// Write the output into the build folder
.pipe(gulp.dest('build/js/'));
});
<script src="js/templates.js"></script>
You may also concatenate into your build output if you like. See gulp-concat
for more info.
<script>
// This will render the template defined by App.header.hbs
document.body.innerHTML = templates.App.header();
</script>
- Source template location: Change the glob passed to
gulp.src()
- Output filename: Change the filename passed to
concat()
- Output directory: Change the directory passed to
gulp.dest()
You can achieve the same result without nested folders. The following directory structure will yield identical output:
├── index.js # The main entry point of your application
└── templates # A folder containing templates named with dot notation
├── App.hbs
├── App.header.hbs
├── App.footer.hbs
├── App.etc.hbs
└── Other.item.hbs
You can then drop the options.processName
function when calling declare()
:
declare({
namespace: 'MyApp.templates',
noRedeclare: true
})
This will result in the same output as the above example, but will nest templates based solely on their filename, ignoring nested folders.