EJS include file relative to project root
Asked Answered
M

6

18

I am using Express and EJS to build a site and I have a directory structure somthing like:

+--www/
  |
  +--partials/
  | |
  | +--header.ejs
  | +--(a bunch of ejs files)
  |
  +--guide
  | |
  | +--index.html
  | +--(other files)
  |
  +--index.html

In both of the index.html files shown in my example, the <% include ... %> command would be different, even if referencing the same included file.

Also if I were to say include header.ejs and then header.ejs has an include for another partial, the whole system breaks down because they are all looking for files in different locations.

To make management easier, I'm trying to find a single string to be able to reference the same included files regardless of what sub-directory the files may be in.

Ideally something like <% include /partials/header.ejs %> would be perfect. But that doesn't work.

Does anyone have any tricks or advice that could give the desired result here?

Migratory answered 20/7, 2016 at 18:17 Comment(4)
Did you find any solution for this? I am trying to reference files in different folders and I think its the same issue.Ashurbanipal
did my solution work for you? #38488480Indoxyl
why has EJS not been updated to accept absolute pathsForeplay
between node, express, and ejs, it seems silly that nothing exposes the path of the webroot in an absolute path... I mean a true absolute path is a filesystem path (from what I understand)... which I can pass from main.js/server.js on to specific routes etc... and then I can likley have an absolute path... but everything has to already know somewhere, the webroot... so why is this not exposed for us to use?!?Mopey
I
8

Looks like this is not supported by EJS however i found a workaround for this problem. In the above setup the pain point is for all partial files you need to mention the relative paths and if you refactor code that becomes more pain. So rather mentioning the relative path at every include i declare a variable rootPath once, and there i give the path to reach home. So that at every include i can simply mention the relative path just like path from root.

For example in guide/index.ejs i mention the following in top of the ejs file

<% var rootPath = '../'; %>

and and code in ejs file looks like below

<%- include(rootPath + 'partials/header'); %>

Your HTML code

<%- include(rootPath + 'partials/footer'); %>

So in case i refactor index.ejs to some other folder all i need to do is change the value of rootPath

Indoxyl answered 14/6, 2017 at 3:38 Comment(0)
H
6

(Other solutions looked a little complicated, so I decided to send my solution too). I tested it on Heroku and it worked fine.

  1. Create app-locals.js and write this inside it:
module.exports = {
  layouts:  process.cwd() + '/views/layouts/',
  partials: process.cwd() + '/views/partials/',
}
  1. In app.js:
// const express = require('express')
// const appLocals = require('./app-locals.js')

// const app = express()
app.locals = appLocals
  1. Use it like:
<%- include(partials + 'header.ejs') %>

Or you can do it like this:

// app-locals.js
module.exports = {
    // Remove the last /
  layouts:  process.cwd() + '/views/layouts',
  partials: process.cwd() + '/views/partials',
}

// app.js
// Add / after the path
<%- include(`${partials}/header.ejs`) %>
  • If you are using this on Heroku, make sure that your files and folder names all are lowercase and that you are using the same case when including the files.
Hag answered 18/3, 2021 at 19:8 Comment(0)
M
3

This is a very good question, it's one I had myself until very recently.

Really there appears to be no direct & central way to get the web root... it's just not something the node nor the express developers thought was valuable it seems.

However there are a few tools made available that can help but won't directly solve the problem.

GENERAL APPROACH:

  1. Get the root from the initial file (where express is instantiated)
  2. Put that into a variable
  3. Get that variable out to your templates

METHOD ONE - globals
The first work around I found is passable. It utilizes the ability to set a variable to the global scope. It's not ideal but if you want to see more click HERE.

IMPORTANT: All of the below require that in the main file of your express app (the one that instantiates the express app) you set a variable to the current working directory.
The three ways I know of are

  1. __directory
  2. path.dirname(require.main.filename);
  3. path.resolve();

I believe there is an advantage to the second method but I don't remember what it is currently... also I'm not sure it works in the latest (14.x) version of node... I started changing over to 3 once I started using 14.5...

METHOD TWO - passing variables
The second workaround I think is the worst... it will work but may become untenable if you have anything less than a simple setup. This method is to pass the variables through your routes. this would look like:

router.get('/', (request, response) => 
{
  response.render('PATH TO TEMPLATE', {dataItem1: 'DATA STUFF TO PASS', dataItem2: 'MORE STUFF'});
});

METHOD THREE
This is definitely the method I prefer. It makes it easier, simpler, and more clean. There are really two methods here but they are very similar.

This method is utilizing either app.locals OR response.locals. When I say app I mean the instance of express, (whatever you called it). So this is as simple as:

  1. Creating a middleware to pass variables in response.locals
  2. Setting variables directly on app.locals
    Examples:
const appDir = path.dirname(require.main.filename);

app.use((request, response, next) => 
{ 
    response.locals.someVar = 'value of someVar'; 
    response.locals.appDir = appDir;
    return next();
});
  1. app.locals.appDir = path.dirname(require.main.filename);//const path = require('path');

In both cases the values are simply available in EJS templates.
So using them would look like:

<%= appDir %>
Mopey answered 27/5, 2020 at 22:33 Comment(1)
FYI using a Windows host, appDir = path.dirname(require.main.filename) evaluated to C:\path\to\files, which caused errors when nodejs evaluated appDir in the EJS template. So instead I used appDir = __dirname; and then the appDir evaluated correctly in the EJS templates.Horthy
J
2

In 2021, October

I'm also having the same issue with importing files on ejs. But I used to figure it out the following way to include my other ejs partials from the parent directory.

Pros

  1. Don't need to set up view engine and directory path on server file.
  2. Independent directory names and locations we can achieve.

Working file: src/views/en/confirmation.ejs

Parent file: src/views/common/footer.ejs (Needs to included on each ejs file)

In src/views/en/confirmation.ejs`

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html data-editor-version="2" class="sg-campaigns" xmlns="http://www.w3.org/1999/xhtml">
     <body>
         <h1>this is body</h1>
     </body>
     
     <% var dir = process.cwd() %>
     <%- include(dir + "/src/views/common/footer.ejs") %>
  
</html>

Related: Error: Could not find the include file "partials/head"

Jaddo answered 12/10, 2021 at 6:33 Comment(0)
S
1

It has been 6 years since this question was asked... but I'd like to post a possible solution for now (2022).

Check the typescript declaration of EJS, I found that it now provides an option named includer wich is a function that allow you to adjust the file path. If all your including files are loacted in includeRoot, then you may write something like this:

ejs.render(htmlString, data, {
    includer: file => {
        // `file` is the string passed to include
        let modifiedFilename = path.resolve(includeRoot, file)
        return { filename: modifiedFilename }
    }
})
Spindry answered 10/11, 2022 at 8:46 Comment(0)
A
0

I would recommend adding the following to your express file:

const includePath = path.resolve('<include dir here>');

Then adding it to your renders:

res.render('example', { val1: 1, val2: 'two', includePath });

And in the files:

include(includePath + '/include.ejs');
And answered 16/4, 2023 at 20:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.