ericf / express-handlebars

A Handlebars view engine for Express which doesn't suck.
BSD 3-Clause "New" or "Revised" License
2.31k stars 384 forks source link

How to use another register function of in if condition ? #226

Open hgurung opened 6 years ago

hgurung commented 6 years ago

This is my helper function.

fileExists: function(path, file) {
        return fs.existsSync(path+'/'+file);
      }

And i want to use it in views like this

{{#if fileExists 'public/uploads' userData.image }}
                  <div class="showImg">
                    <img src="/public/uploads/{{../userData.image}}">
                  </div>
                  {{/if}}

I will also have else condition to put default image if not found.

romellem commented 5 years ago

There are two ways you could accomplish this:

  1. With a new block helper.
  2. With a subexpression.

Option 1, Block Helper

Helpers come in two forms: inline or block. As you've defined your fileExists helper, it only works as an inline helper. To change it to a block helper format, you'd need an aditional argument (typically named options) that gets two special attributes added to it: fn() and inverse(). Its a bit easier if you see the code:

fileExists: function(path, file, options) {
  var file_exists = fs.existsSync(path + '/' + file);
  if (file_exists) {
    return options.fn(this);
  } else {
    return options.inverse(this);
  }
}); 

Then you'd invoke the fileExists block helper like this:

{{#fileExists 'public/uploads' userData.image }}
  <div class="showImg">
    <img src="/public/uploads/{{../userData.image}}">
  </div>
{{else}}
  File does not exist!
{{/fileExists}}

Options 2, Subexpression

Alternatively, Handlebars allows for subexpressions within a helper. In this case, we already have the if block helper, so we could keep your existing fileExists inline helper as is and pass its return value (either true or false) as an argument into the {{#if}} block helper as a sub expression. Sub expressions are called via parens, like so:

{{#if (fileExists 'image.jpg')}}
  <div class="showImg">
    <img src="/public/uploads/{{../userData.image}}">
  </div>
{{else}}
  File does not exist!
{{/if}}

I made a quick example app, with my server app.js and my template home.handlebars shown below. Note for testing purposes, the only files that will "exist" in my code are image.jpg and doc.pdf.

app.js

/* app.js */
const express = require('express');
const exphbs  = require('express-handlebars');

const app = express();
const hbs = exphbs.create({
    defaultLayout: 'main',

    // Dummy helpers that work with the filenames 'image.jpg' and 'doc.pdf'
    helpers: {
        fileExistsInline: function(path) {
            return ['image.jpg', 'doc.pdf'].includes(path)
        },
        fileExistsBlock: function(path, options) {
            let file_exists = ['image.jpg', 'doc.pdf'].includes(path);
            if (file_exists) {
                return options.fn(this);
            } else {
                return options.inverse(this);
            }
        },
    }
});

// Register `hbs.engine` with the Express app.
app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');

app.get('/', function (req, res) {
    res.render('home');
});

app.listen(3000, () => {
    console.log('Listening on port 3000!');
});

home.handlebars

{{! home.handlebars }}

<h1>Using built-in <code>if</code> block helper and sub expression <code>fileExistsInline</code> helper</h1>

<blockquote>
    <h2>Image:</h2>
    {{#if (fileExistsInline 'image.jpg')}}
      <b>Image file here!</b>
    {{else}}
      <i>No image found!</i>
    {{/if}}
</blockquote>

<blockquote>
    <h2>Video:</h2>
    {{#if (fileExistsInline 'video.mp4')}}
      <b>Video here!</b>
    {{else}}
      <i>No video found!</i>
    {{/if}}
</blockquote>

<hr>

<h1>Using custom <code>fileExistsBlock</code> block helper</h1>

<blockquote>
    <h2>Image:</h2>
    {{#fileExistsBlock 'image.jpg'}}
      <b>Image here!</b>
    {{else}}
      <i>No image found!</i>
    {{/fileExistsBlock}}
</blockquote>

<blockquote>
    <h2>Video:</h2>
    {{#fileExistsBlock 'video.mp4'}}
      <b>Video here!</b>
    {{else}}
      <i>No video found!</i>
    {{/fileExistsBlock}}
</blockquote>

Results in the following HTML:

<h1>
    Using built-in <code>if</code> block helper and sub expression
    <code>fileExistsInline</code> helper
</h1>

<blockquote>
    <h2>Image:</h2>
    <b>Image file here!</b>
</blockquote>

<blockquote>
    <h2>Video:</h2>
    <i>No video found!</i>
</blockquote>

<hr />

<h1>Using custom <code>fileExistsBlock</code> block helper</h1>

<blockquote>
    <h2>Image:</h2>
    <b>Image here!</b>
</blockquote>

<blockquote>
    <h2>Video:</h2>
    <i>No video found!</i>
</blockquote>

Which looks like this:

helpers-example