express-handlebars & webpack - can't export partials and helpers in multiple paged app

2.2k views Asked by At

I am using handlebars for my templating needs. I obviously need to render some templates on the server to handle building standard layout (with header, footer, navbar and content panel etc). But I also require some client side templating so that I can use ajax to fetch data and then render it in templates.

I chose the express-handlebars module in node because it automatically compiles templates and layouts in the server.

Several articles suggested that I can configure webpack to export my templates, partials and helper to the client so I can reuse them.

So I created the following webpack config:

et path = require('path');

module.exports = {
    entry: {
     login: './public/js/users/login.index.js',
        logout: './public/js/users/logout.index.js',
        newListing: './public/js/listing/newListing.index.js',
        listingCollection: './public/js/listing/newListing.index.js',
        listingDetails: './public/js/listing/listingDetails.index.js',
        userRegistration: './public/js/listing/newListing.index.js',
 },
 output: {
        path: path.join(__dirname, '/public/js/bundles'),
        publicPath: "/public/",
  filename: "[name].bundle.js"
 },
    module: {
        rules: [
            {
                test: /\.handlebars$/,
                include: [
                    path.resolve(__dirname, 'views/listing')
                ],
                loader: "handlebars-loader"
            }
        ]
    }
};

I don't want a single paged app so I have configured to export just the necessary .js file bundled with each page.

My app.js fragment responsible for the express-handlebars config is as follows:

const exphbs = require('express-handlebars');
const routes = require('./app/routes');
const app = express();
const hbs = exphbs.create({
    defaultLayout: 'main',
    partialsDir: [
        'views/partials',
        'views/partials/common',
        'views/partials/listing/paymentsAndExchange'
    ],
    helpers: {
        isListingType: function (v1, v2, options) {
            if (v1 === v2) {
                return options.fn(this);
            }
        }
    }
});
app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');

My problem is that none of these things are exported to the client/browser (partials and helpers - I have examined the exported bundles and they are definitely not available).

Eventually I gave up trying to get this to work and resorted to just recreating the partials in the client by writing the markup inside script tag as follows:

<script id="a-template" type="text/x-handlebars-template">
  {{#unless somecondition}}
    Say this
  {{/unless}}
</script>

And then I get a reference to this script and compile the template and inject data etc as per the usual method.

But...this is not possible because the server side template compilation parses this script as though it was part of the page view and by the time its rendered in the client I have lost all of the handlebars notation {{}}.

Ideally I just want to be able to configure express-handlebars / webpack combination to export the correct partials and helpers with each page so that I can just use them.

Lots and lots of people must have struggled with this exact use case because handlebars is very popular and I know it isnt just supposed to be used in SPA's.

Id be really grateful if anybody has any suggestions for me.

1

There are 1 answers

0
apostrophedottilde On

For anybody who has been struggling with this type of problem. I needed to correctly configure the webpack to know where to find my partials and helpers (I though express-handlebars config did this, but that only applies to serverside).

let path = require('path');

module.exports = {
    entry: {
     login: './public/js/users/login.index.js',
        logout: './public/js/users/logout.index.js',
        newListing: './public/js/listing/newListing.index.js',
        listingCollection: './public/js/listing/newListing.index.js',
        listingDetails: './public/js/listing/listingDetails.index.js',
        userRegistration: './public/js/listing/newListing.index.js',
 },
 output: {
        path: path.join(__dirname, '/public/js/bundles'),
        publicPath: "/public/",
  filename: "[name].bundle.js"
 },
    module: {
        loaders: [{
            test: /\.handlebars$/,
            loader:  "handlebars-loader",
            query: {
                partialDirs: [
                    path.join(__dirname, 'views', 'partials')
                ],
                helperDirs: [
                    path.join(__dirname, 'views', 'helpers')
                ]
            }
        }]
    }
};

This makes the compiled partials and helpers available in the client, but I then still need to register them in client code.

let mangaDetailsPartial = require("../../../views/partials/manga_specific_details.handlebars");
let animeDetailsPartial = require("../../../views/partials/anime_specific_details.handlebars");
let itemSpecificsPartial = require("../../../views/partials/listing/itemSpecifics.handlebars");

Handlebars.registerPartial("manga_specific_details", mangaDetailsPartial);
Handlebars.registerPartial("anime_specific_details", animeDetailsPartial);
Handlebars.registerPartial("item_specifics", itemSpecificsPartial);

Now everything works. There are some example in a link at the bottom of the npm page for 'handlebars-loader' module.