mde / ejs

Embedded JavaScript templates -- http://ejs.co
Apache License 2.0
7.71k stars 846 forks source link

EJS conditional rendering not working as expected #641

Closed gaurav6386 closed 2 years ago

gaurav6386 commented 2 years ago

I'm trying to implement this code but it doesn't work as expected. The problem is explained below.

<% if (Object.keys(notes).length !== 0) { %>
<p class="card-text w-100" id="bookShelf">
   <div class="inline-block">
   <% notes.forEach(note => { %>
      <div class="card ml-2" style="width: 18rem;">
         <div class="card-body">
            <h5 class="card-title"><%= note.title %> </h5>
            <p class="card-text"><%= note.body %> </p>
         </div>
      </div>
   <% }); %>
   </div> 
</p>
<% } else { %>
<p class="card-text w-100" id="bookShelf" aria-placeholder="Add some notes...">
   You have not added any notes
</p>
<% } %> 

In the above code, the 'notes' object is served from the back-end and is not empty. When rendering this ejs code I get the output in such a way that the last 'card-text' paragraph element still get served to the front-end with no element. And first 'card-text' paragraph element gets displayed separately instead of enclosing the div element containing notes. In an ideal case, it is expected to display either one of the paragraph elements but not both. Please refer to the screenshots attached for a better understanding of the problem. I already have spent too much time on this and still couldn't figure out the issue unless it is bug of some type. Any suggestion or help however small will be appreciated.

Inspect tools snap webpage sc1 webpage sc2

mde commented 2 years ago

I would have to do more diagnostic work to be sure, but why are you checking the length of Object.keys of notes, if it's an Array? In theory there shouldn't be other enumerable properties on there, but who knows? You should just be checking the length of the array. And you don't need a strict equality check — numbers greater than zero are already truthy.

mde commented 2 years ago

Often it's helpful to boil stuff down to less code. You can see below that a minimal example of forEach functions as expected in EJS:

> let ejs = require('./ejs');
undefined
> ejs.render('<div><% if (!arr.length) { %><div>No items</div><% } else { %><div><% arr.forEach((item) => { %><p><%= item %></p><% }); %></div><% } %></div>', {arr: ['A', 'B', 'C']});
'<div><div><p>A</p><p>B</p><p>C</p></div></div>'

Maybe you can remove some of the extra DOM elements, and that will give you an idea what's going on with your code. Also might be helpful to inspect your notes object to make sure exactly what it has on it. This is clearly not an issue with the library itself.

Since this is not an actual issue with EJS, I am closing this issue. Please feel free to reopen if you create a minimal failing example that shows a problem with the library.

gaurav6386 commented 2 years ago

Hi @mde ... Thanks for responding. I was just trying to figure out what was causing this issue so I changed the usual conditional checking with Object.keys().length()===0. Also there was nothing wrong with the notes object. I tried to tinker with DOM elements. And as soon as I replaced <p> with <div> element and removed the following div element it starts working. Modifications are as shown below

<% if (notes) { %>
   <div class="card-text inline-block" id="bookShelf">
   <% notes.forEach(note => { %>
      <div class="card ml-2" style="width: 18rem;">
         <div class="card-body">
            <h5 class="card-title"><%= note.title %> </h5>
            <p class="card-text"><%= note.body %> </p>
         </div>
      </div>
   <% }); %>
   </div> 
<% } else { %>
<p class="card-text w-100" id="bookShelf" aria-placeholder="Add some notes...">
   You have not added any notes
</p>
<% } %> 

And I'm still not sure if this was an inherent bug of EJS due to excessive nesting or some hidden error. I will check it out again some other time.