• Stars
    star
    804
  • Rank 56,681 (Top 2 %)
  • Language
    JavaScript
  • Created about 13 years ago
  • Updated almost 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A plugin for handlebars in require.js (both in dev and build)

Require.js Handlebars Plugin

devDependency Status

Requirements

Should work in both the java and node build environments.

Require.js >= 2.1.x (The last tag to work for Require < 2.1 is the 0.3.3 tag)

Usage

Write a template ( path: App/Template/One.hbs ):

<div class="best plugin ever">
  This is my {{ adjective }} template.

  {{! To include a partial: }}
  {{! just provide the path to the partial without the extension }}

  {{> App/Template/CoolPartial }}

  {{! the path can also be relative to the current template: }}
  {{> ./coolPartial }}
</div>

Here's the partial (optional) ( path : App/Template/CoolPartial.hbs )

<div>
  {{! This can obviously have it's own partials, etc, etc }}
  I am a partial
</div>

Installation

Clone this repo* or use bower to add require-handlebars-plugin to your project (typically in you lib/ directory) and make sure you tell requirejs about the new hbs plugin by editing your requirejs.conf.js file (you can also pass a few options):

require.config({
	paths: {
		hbs: 'lib/require-handlebars-plugin/hbs'
	},
	hbs: { // optional
		helpers: true,            // default: true
		templateExtension: 'hbs', // default: 'hbs'
		partialsUrl: ''           // default: ''
	}
});

partialsUrl: base url for loading partials so that you don't have to provide the full path every time you need to load a partial within a template.

Then require your templates like so:

require(['hbs!App/Template/One'], function ( tmplOne ) {
  // Use whatever you would to render the template function
  document.body.innerHTML = tmplOne({adjective: "favorite"});
});

And then the output into your body would be as follows:

<div class="best plugin ever">
  This is my favorite template.

  <div>
    I am a partial
  </div>
</div>

YAY!

* Note that if you clone the require-handlebars-plugin repository into an existing git repository, the existing repo will not include the files within the newly-cloned require-handlebars-plugin repo, because git ignores all subfolders which are git repos of their own. Look into git's submodules feature as a way to solve this issue.

i18n

As of the latest version. This functionality has been removed. Probably use format-js for this.

Helpers

Just put your helpers in templates/helpers/* and they'll automagically get pulled in as long as you write them as modules.

I find that many helpers are good helpers in regular code as well, so the following is a good practice:

define('templates/helpers/roundNumber', ['handlebars'], function ( Handlebars ) {
  function roundNumber ( context, options ) {
    // Simple function for example
    return Math.round( context );
  }
  Handlebars.registerHelper( 'roundNumber', roundNumber );
  return roundNumber;
});

Then in your templates, you can just do:

{{roundNumber Data.ThreeFourths}}

The system will make sure these modules are pulled in automatically from that directory. But if in your app, you need a rounding module (perhaps in a view/datanormalization place), you could do this:

require(['templates/helpers/roundNumber'], function ( roundNumber ) {
  var threeFourths = (3/4);
  alert( roundNumber( threeFourths ));
});

It's just a module that happens to register itself.

You can specify a helper path callback in the config. The callback should be a function that gets a name of a helper as the only argument and returns the full path to be require()-d, e.g., the following callback allows for automatic loading of helper modules written in CoffeeScript (via the require-cs plugin) under a non-standard location:

require({
  hbs : {
    helperPathCallback: function(name) {return 'cs!/helpers/' + name;}
  }
}, ['main'])

Meta Data

Any template that begins with a comment, with only a valid json object in it will be read in as meta data for the template.

I encourage you to list the name of the template and give a description, though these aren't strictly necessary.

Styles

If you want to build stylesheets that are comprised of only styles needed by the templates that your app uses, I encourage you to add a styles property to the meta info:

{{!
{
  "name" : "template1",
  "description" : "A nice template.",
  "styles" : ["templatecss"]
}
}}

This will inject a link tag in dev mode to load in this style dynamically. At build time, a screen.build.css is created. At this time it is just a list of import statements. These can be inlined by many existing tools. Eventually I'd love it to just happen.

De-duping happens automatically, so don't worry if multiple templates require the same styles. The styles are injected in the order that they are read in, so usually from least specific to most specific. This is usually what you want, but know that if you do weird things, it could break.

Introspection

In dev mode a few properties are added to your function (an object in javascript) as a helper with debugging and as a testing plug-point.

Those variables look like the following:

require(['hbs!template/one'], function ( tmplOne ) {
  console.log(
    'Variables referenced in this template: ',                     tmplOne.vars,
    'Partials/templates that this file directly depends on: ',     tmplOne.deps,
    'Helpers that this template directly depends on: ',            tmplOne.helpers,
    'The metadata object at the top of the file (if it exists): ', tmplOne.meta
  );
});

Note: All of these go away after a build, as they just take up space with data that is known at build time, which is the ideal time to get stuff figured out (speed-wise).

Builds

As long as all of your paths match up, this should precompile all of your templates and include them in the build.

You can stub out the hbs plugin in the build using stubModules the same way you would for other plugins that inline the final function. Your helpers and compiled templates still need to load Handlebars though, so you'll need to make sure they load the runtime version of Handlebars included in this repo as hbs/handlebars.runtime.js. You just need to do 2 things for that in your build config:

  1. Make sure hbs.handlebarsPath resolves to hbs/handlebars.runtime.js
  2. Make sure your helpers load the same file

See the example build configuration for a way to do that by setting handlebarsPath to handlebars, having the helpers load handlebars, and setting handlebars to hbs/handlebars.runtime in the paths.config

Before Build

Before Build

After Build

After Build

So many dependencies in the hbs plugin!

I use them for coding happiness. It shouldn't bother you tooooo much, because it all gets built out in production. The hbs.js file essentially gets written to the main.js file as a noop (a few empty definitions), and none of it's dependencies are included into the build. All the dependencies are inside the hbs folder and this folder should be a sibling of the hbs.js file.

Demo

To run the demo, go into the root directory of this project and run the following command.

./build.sh

This requires that node.js is installed. To see these in your browser, I'd suggest serving them quickly with the python simple server. (Linux/OSX assumed here, but there is a java implementation of the require.js build that should work just as well as the node version. I have not tried it though.)

cd ~/require-handlebars-plugin
python -m SimpleHTTPServer

You could also use the node 'serve' module.

npm install serve -g
serve .

Then visit http://127.0.0.1:8000/demo.html for the dev version.

And visit http://127.0.0.1:8000/demo-build.html for the production build version.

You should be able to see all of the templates and individual files in your network panel in dev mode, and just 2 minified files in build mode.

Config

There are several configurable options, which you can set in your require.config:

require.config({
  // ... other require config here

  // hbs config
  hbs: {
    helpers: false,               // When false, won't look for and try to automatically load
                                  // helpers (true by default)

    helperPathCallback:           // Callback to determine the path to look for helpers
      function (name) {           // ('/templates/helpers/'+name by default)
        return 'cs!' + name;
      },

    templateExtension: "html"     // Set the extension automatically appended to templates
                                  // ('hbs' by default)

    handlebarsPath:               // Custom path to handlebars for compiled templates
      'some/path/to/handlebars'   // ('hbs/handlebars' by default). Could simply be 'handlebars'
                                  // as long as it is defined in paths. If you are stubbing out
                                  // the plugin in an optimized build, use this to point to 
                                  // the runtime hbs (see demo/app.build.js)

    compileOptions: {}            // options object which is passed to Handlebars compiler
  }

})

Notes/QA

Partial Collision

This plugin registers every single template as a partial with it's modified module name and no file extension.

App/Template/One.handlebars is registered as App_Template_One

I'd encourage you to not call registerPartials in your code, and just use the automatic module registering, that way you definitely won't hit any collisions. You could also just be careful. We're all adults here.

Templates not loading cross-domain

In dev mode, loading the templates requires that you are on the same domain as your templates. This is standard same origin policy stuff. Once you build, though, it won't matter since there are no additional requests. Usually a few cleverly placed host overrides get you through the dev mode hurdles.

Other Templating Languages

Very little of this is specific to handlebars, but things are just a tiny bit too specific about how everything works to properly generalize this.

If you'd like to implement this for your templating language of choice, you'll need:

  • Has a pre-compile type functionality (unless you don't care about builds)
  • If it has some concept of partials, that you can register them externally
  • It eventually returns a function that takes data context and outputs something you can deal with.
  • For any of the meta-data, you'll need some fancy regex or an AST to walk through.

I'd just turn your template language into a module first (just the old global name, or whatever), then look through the references to Handlebars in hbs.js and see if your templating language does something similar. It's not a terribly complicated process.

License

Most of the code in this is from James Burke and Yehuda Katz in require.js and handlebars.js (respectively). Those projects are under their own license. Any other code added by me is released under the WTFPL license.

More Repositories

1

yepnope.js

A Script Loader For Your Conditional Builds
JavaScript
2,514
star
2

css-colorguard

Keep a watchful eye on your css colors.
JavaScript
2,444
star
3

listpattern

A JavaScript List Pattern Formatter Library
JavaScript
33
star
4

uglifui

Totally in-browser (static) uglifyjs thing (hopefully)
JavaScript
21
star
5

CSSCSS

CSS Quine
17
star
6

node-538

Get the 2016 presidential predictions from 538 in your console
JavaScript
16
star
7

rework-pseudo-classes

Add companion classes to your pseudo styles for testing and convenience.
JavaScript
12
star
8

static-build

My current personal starter static folder with build scripts. Sproutcore on Require with Compass.
JavaScript
11
star
9

dfdep

Deferred Dependencies In The Browser
11
star
10

gaga.js

A Poker Utility Library for JavaScript
JavaScript
11
star
11

sc-handlebars

Compile Sproutcore Handlebars Templates on the server
JavaScript
10
star
12

AssetRace

How fast can you load the same set of resources?
JavaScript
10
star
13

vanilla-extract-react-bake

A variant-focused typesafe component factory for vanilla extract recipes, obviously
TypeScript
10
star
14

yayquery_site

The code for the yayquery podcast website
PHP
9
star
15

mfbars

MessageFormat Handlebars Integration
JavaScript
9
star
16

node-analytics

Real-time + Roll Up
9
star
17

goto.js

Add a goto implementation to JavaScript!
JavaScript
9
star
18

abusing-preprocessor-asts

One of the coolest benefits of preprocessors is that they have easy-to-grok open source parsers that you can actually get into. By injecting yourself after the pre-CSS code has been parsed, you can have a data-centric view of your styles. You can use this information to reduce complexity, point out probable mistakes, or more interestingly, gather metrics, and implement real-time update mechanisms on the client side.
JavaScript
8
star
19

fad.js

Offload all ad code on your site to a single iframe so it doesn't slow things down.
JavaScript
6
star
20

rhokgoh

Glimmer Of Hope Donation Site for Random Hacks of Kindness Austin
JavaScript
6
star
21

xqtouch

A plugin for xui that exposes the exact api needed to run jqTouch.
JavaScript
6
star
22

jQuery-Uncovered

Comment on the lines of jQuery to gain understanding of them
5
star
23

modern-webapp-structure-and-deployment

Future Insights Live - Modules, Builds and Perf
JavaScript
5
star
24

jira2sprintly

A Script to help migrate all your tickets from greenhopper / JIRA to the awesome sprint.ly
JavaScript
5
star
25

minidonations-ui

minidonations-ui-code
JavaScript
5
star
26

broccoli-colorguard

Broccoli wrapped CSS Colorguard
JavaScript
4
star
27

blog

My personal blog
JavaScript
3
star
28

jq-best-friends

Presentation Files
2
star
29

fusejs

Alpha version of FuseJS
JavaScript
2
star
30

books.jquery.com

JavaScript
2
star
31

txjs2012

2
star
32

txjs2011_site

JavaScript
2
star
33

uxoflanguagetalk

The UX of Language slide deck
JavaScript
2
star
34

txjs2013

Texas JavaScript 2013 Sit
JavaScript
2
star
35

pooping-out-css

Talk on CSS Preprocessors for CSSConf.eu
JavaScript
2
star
36

MVCModuleMagicTalk

Talk from BackboneConf 2012
JavaScript
2
star
37

hacking-parsers-and-compilers-in-js

JSConf Brazil 2013 Talk
JavaScript
2
star
38

dabl.js

Data Abstraction Library aka the Brent Spiner Library
1
star
39

scratchpad

Random stuff I don't want to lose.
JavaScript
1
star
40

txjs_site

site for txjs
1
star
41

fbeventpicker

Pull your facebook events and choose which ones to output
1
star
42

jQuery.placeHoldize

jQuery.placeHoldize is a jQuery plug-in which simulates the HTML5 "placeholder" attribute for every browser which is not WebKit-powered. It also simulates it for "textarea"s as the WebKit only support it on "input"s.
JavaScript
1
star