32

I am working on a template where I am trying to render template using express and ejs. As to the standard structure of node app, I have app.js file which which contains functions like following:

app.locals.getFlag = function(country) {
var flag_img_name = "";
if (country.toLowerCase() == "us") {
    flag_img_name = "flag_us16x13.gif";
}   
else if (country.toLowerCase() == "ca"){
    flag_img_name = "flag_ca16x13.gif";
}
return flag_img_name;
}

I have some_template.ejs file which calls this function like follows:

<img src="http://some_url_path/<%=getFlag(data_point[0].country_name) %>" width="16" height="14" alt="country" >

and it works just fine. However, I have around 15-20 functions like this and I don't want to define all of them in app.js. Is there any other place where I can define these functions and call them in the template same way as I am doing now? If yes, what would be the way to define them so that they are accessible like they are right now.

I am new to node, express and ejs and not sure of different techniques. If someone could shed a light over it, it would be great. Thank you in advance.

LearningAsIGo
  • 1,240
  • 2
  • 11
  • 23

7 Answers7

52

Just posting this answer here for someone who would might end up on this question while resolving same issue.

All you have to do is create new file (say functions.ejs) and include it in the .ejs file where you want to call that function. So, I have function like this in file named functions.ejs:

<%
getPriceChgArrow = function(value) {
    arrow_img_name = "";
    if (value < 0) {
        arrow_img_name = "arrow_down12x13.gif";
    }
    else {
        arrow_img_name = "arrow_up12x13.gif";
    }
    return arrow_img_name;
}
%>

Then include functions.ejs into the file you want to call function from. Say, I want to call this function in quote.ejs file. So, I would include it as follows:

<% include *file_path*/functions %> 

Just use this function at appropriate location in your ejs file from where you want to call it. For example:

<img src = "http:/some_url/<% getPriceChgArrow(data_point[0].value) %>" />

and you are all set. Hope this helps someone.

LearningAsIGo
  • 1,240
  • 2
  • 11
  • 23
39

Create common functions in js file helper.js.

function common1() {
    //function body
}
function common2(key) {
    //function body
}
module.exports = {
    common1: common1,
    common2: common2
}

And then require this file in your node function

var helper = require('./helper');

and then pass this helper with ejs render

res.render('index', { helper:helper });

And use your function is ejs file

<%= helper.common1() %>

That's It

Faaiq
  • 391
  • 4
  • 5
  • For me doesn't work; module.exports shouldn't have key value pairs. @Rik's comment below shows correct module.exports formatting. – J.E.C. Feb 25 '20 at 12:42
  • 3
    module.exports does in fact have key-value pairs; the alternate syntax of `module.exports = { oneFn, anotherFn }` is taking advantage of an ES6 feature called [Enhanced Object Literals](http://www.benmvp.com/learning-es6-enhanced-object-literals/), specifically property value shorthand. The key-value pairs are just happening transparently. – Tim Mar 24 '20 at 18:15
  • This worked for me. I was able to easily pass a reference to a Functions file I use in Models and Controllers to the EJS context like: `res.status(200).render('view', { Functions: Functions, other:data })` – ow3n Jul 24 '20 at 17:01
  • 1
    This worked for me and should be the accepted answer tbh. I struggled way to long to try and get the accepted answer to even compile. I had this answer working and refactored within 30mins and is a great way of doing this. – Kpizzle Feb 23 '21 at 00:09
  • Even now that <%= helper.common1 %> prints the function definition. When I try to use the function with a button in the ejs file. It doesn't work. – Luis Donaldo García Castro Jul 27 '22 at 03:51
13

Well, for some reason the accepted answer didn't worked out for me. Also it doesn't makes sense to create a separate *.ejs file for each of my functions and then importing the file in view - specially when I have very simple logic to be implemented.

In fact it is very simple and easy to define function and use it in the view

I did this:

<%
   // ------ Define a function
   get_tree = function(tree){
      for(var i in tree){
%>
     <%= tree[i].title %>
<%
      }
   }
  // ----- Call the above declared function
  get_tree(tree);
%>

And it works!

Thanks

Siddhartha Chowdhury
  • 2,724
  • 1
  • 28
  • 46
  • 3
    Yes your approach makes sense too. The only reason I created separate functions.ejs file is because I wanted to keep all my functions together. The one I have shown below is just one of the many functions that I am using throughout application. – LearningAsIGo Feb 27 '17 at 18:32
  • Okay I understand but, in first of all its not a good practice to have logic on "view", well of course there cud be exceptions but, again all computations and calculations should be implemented in logic side, once done then you send it to view to " render". – Siddhartha Chowdhury Feb 28 '17 at 05:04
7

You can just require a separate file and set app.locals to this

app.locals = require('./path/helpers')

In helpers.js:

getFlag = function(country) {
var flag_img_name = "";
if (country.toLowerCase() == "us") {
    flag_img_name = "flag_us16x13.gif";
}   
else if (country.toLowerCase() == "ca"){
    flag_img_name = "flag_ca16x13.gif";
}
return flag_img_name; 
}
anotherFunction=function(x){
return 'hello '+x
}

   module.exports={getFlag, anotherFunction}
Rik
  • 1,870
  • 3
  • 22
  • 35
  • in my controller action, `ReferenceError: app is not defined` . Where do you put your first snippet? (That is to say: where should app.locals be assigned?) – dcorking Mar 05 '20 at 12:11
  • To answer my own comment, anywhere where `const app = express();` is defined or imported, such as app.js itself. – dcorking Mar 05 '20 at 13:25
3

It seems the easiest way to do this is to pass the function in attached to the object with all the data for the render:

in your js:

const data = {
  ...all other data,
  getFlags: function(country) {
    var flag_img_name = "";

    if (country.toLowerCase() == "us") {
      flag_img_name = "flag_us16x13.gif";
    } else if (country.toLowerCase() == "ca"){
      flag_img_name = "flag_ca16x13.gif";
    }

    return flag_img_name;
  }
};

ejs.render(template, data);

in your template:

<img src="http://some_url_path/<%=getFlag(data_point[0].country_name) %>" width="16" height="14" alt="country" >
2

The order you setup your file has an importance on how the functions are defined. The execution is top to bottom not on document evaluation. Example below to setup your functions.

document.html

<section class="container">
    <%- include('./helpers/common') %>
    <%- include('./home') %>
</section>

common.ejs

<%
MyFunction = function() {
    // Write your code here
}
%>

home.ejs

<% if(MyFunction() ) { %>
    <!-- Write your HTML markup -->
<% }%>
Rick
  • 12,606
  • 2
  • 43
  • 41
-1

In a js file, create a instance of the function like: if your function name is test (), Var ren = test (); will create a reference to this function.

Before rendering data to the ejs page add the reference variable ren to that object:

Data.ren = ren();

Res.render(Data)

And now in ejs while when you call <% ren() %> it will call the fonction.

Ivan Gabriele
  • 6,433
  • 5
  • 39
  • 60
Sod
  • 1
  • 1