RandomEtc / ejs-locals

Express 3.x layout, partial and block template functions for the EJS template engine.
298 stars 63 forks source link

paths for includes/partials in includes/partials #13

Closed TimNZ closed 11 years ago

TimNZ commented 12 years ago

The resolved paths of includes/partials inside a view that is itself an include/partial is relative to the original view and not the current rendering view.

I tried a quick debug trace and I'm not sure if it's ejs or ejs-locals

RandomEtc commented 12 years ago

I think I understand the issue. To confirm, can you post a complete example? Or better, fork and add a failing test?

It's definitely an ejs-locals issue, since we handle path resolving ourselves in the include and partial functions.

TimNZ commented 12 years ago

Cool, I posted this in ejs issues but had decided it was ejs-locals.


The below directory hierarchy is impossible to support even though it seems to make sense to me.

views -> home.ejs -> account ------> accounthome.ejse -> inclues ------> header.ejs ------> defaultsidenav.ejs ------> sidelisting.ejs

I have <%- include includes/defaultsidenav %> in home.ejs I have <%- include includes/sidelisting.ejs %> in defaultsidenav.ejs I have <%- include ../includes/defaultsidenav %> in account/accounthome.ejs home.ejs will load fine account/accounthome will fail as includes/defaultsidenav will fail as it can't find includes/sidelisting.

Shouldn't includes/partials be relative to the current view as opposed to the original view? I should just be able to have <%- include ./sidelisting.ejs %> in defaultsidenav.ejs and it is always found regardless of parent.

Hopefully I'm just doing something wrong here as otherwise it looks like I have to store all views in one directory with one includes directory.

Alternative is to support /absolute path relative to engine views directory.

TimNZ commented 12 years ago

Man I really need to make an effort to learn JS properly if I'm going to be spending this much time trying to diagnose problems! :)

The issue is this: index.js - Line 84: options.locals.partial = partial.bind(options);

It is bound the initial options object, so filename is always the initial view.

Will test and do a pull request if everything works as expected.

ralyodio commented 11 years ago

I am also having this problem.

I have a partial included from two different views, which includes another partial This second level partial is looking in the wrong place. It is looking relative to the parent include, not the file that included it.

./views/index.ejs
   loads partial: ./views/profile/person.ejs

./views/profile/index.ejs
   loads partial: person.ejs

./views/profile/person.ejs
   loads partial: button.ejs

it is looking for button.ejs in ./views when I use the partial in index.ejs and it finds it correctly in ./views/profile/ when I load it from profile/index.ejs

ralyodio commented 11 years ago

I dug around the code, but it seems the function partial() doesn't keep track of what called it. It always assumes the original caller, not a nested one.

Here's a stack trace.

Error: /home/ettinger/projects/wishd.me/wishd/views/index.ejs:21
    19| <h2>Top members</h2>
    20| <ul id="active-users" class="list">
 >> 21| <% users.forEach(function(user){ %>
    22| <%- partial('profile/person', { person: user }) %>
    23| <!--
    24|     <li>

/home/ettinger/projects/wishd.me/wishd/views/profile/person.ejs:8
    6|              <div class="username"><%= person.username %></div>
    7|          </a>
 >> 8|          <small><%- partial('follow-button', { owner: person } ) %></small>
    9|      </div>
    10|         <div class="stats">
    11|             <p><a href="/<%= person.username %>/lists"><% include ../stats/lists %></a></p>

Could not find partial follow-button
    at Object.partial (/home/ettinger/projects/wishd.me/wishd/node_modules/ejs-locals/index.js:312:11)
    at eval (eval at <anonymous> (/home/ettinger/projects/wishd.me/wishd/node_modules/ejs/lib/ejs.js:234:12))
    at exports.compile (/home/ettinger/projects/wishd.me/wishd/node_modules/ejs/lib/ejs.js:239:15)
    at Object.exports.render (/home/ettinger/projects/wishd.me/wishd/node_modules/ejs/lib/ejs.js:277:13)
    at render (/home/ettinger/projects/wishd.me/wishd/node_modules/ejs-locals/index.js:332:20)
    at Object.partial (/home/ettinger/projects/wishd.me/wishd/node_modules/ejs-locals/index.js:374:12)
    at eval (eval at <anonymous> (/home/ettinger/projects/wishd.me/wishd/node_modules/ejs/lib/ejs.js:234:12))
    at Array.forEach (native)
    at eval (eval at <anonymous> (/home/ettinger/projects/wishd.me/wishd/node_modules/ejs/lib/ejs.js:234:12))
    at exports.compile (/home/ettinger/projects/wishd.me/wishd/node_modules/ejs/lib/ejs.js:239:15)

You can see its looking for follow-button relative to the index.ejs, not person.ejs I suspect a nested include will have same problem as partial, but I have not tested.

I think if there is a / preceding path name, we should look from the ./views/ directory, then it would work everywhere.

TimNZ commented 11 years ago

Correct read my earlier comment. It fixes partial so it works nested, I don't bother using include.

ralyodio commented 11 years ago

I don't see a fix anywhere, just that line 84 was the problem. Did you paste one?

TimNZ commented 11 years ago

Fair enough, I thought that was the replacement line.

options.locals.partial = partial; //.bind(options);

ralyodio commented 11 years ago

Can you submit a pull request? I'd like to get this into the official package if it works.

ralyodio commented 11 years ago

This did not work for me, it still couldn't find the 2nd partial relative to the 1st partial.

TimNZ commented 11 years ago

I just did a diff and that is definitely the only difference between my version and master.

It might be because you need to prepend ./ to views for relative paths to work

e.g. partial('./nestedview')

RandomEtc commented 11 years ago

I just added a test for calling partials from sub directories (see /test/fixtures/blog). To make it pass I had to fix the issue with partial.bind - the partial function itself now re-binds options so that they are correct when calling a partial from a partial. I hope this fixes the issue!

RandomEtc commented 11 years ago

(Published as ejs-locals 1.0.1 - note that include has been removed and you should use the include directive from ejs itself if you need that functionality... apologies for different syntax)

Vadorequest commented 10 years ago

I have a similar issue that drives me crazy. See http://stackoverflow.com/questions/25438015/use-ejs-locals-partials-with-data-json-typeerror-converting-circular-structur

The path is random, sometimes based on the caller, sometimes based on the layout and it changes when i save the file. Is it possible to use a non-relative path?