3

Inside ejs forEach loop I have elements with links, e.g.:

<% someArray.forEach(function(someElement) { %>
    <div>
        <h5>someElement.title</h5>
        <a href="#" onclick="<% doSomeAction(someElement) %>">Some Action</a>
    </div>
<% }); %>

When I click on link on one element, it triggers onclick event on all elements in the list. So let's say my Express code looks like this:

let someArray = [
    {
        title: 'Title 1',
        body: 'This is element 1'
    },
    {
        title: 'Title 2',
        body: 'This is element 2'
    }
];
let doSomeAction = (someElement) => {
    console.log(someElement.title);
};

If I click on element where title is "Title 1", this is what is going to get logged in the console:

Title 1
Title 2

Can anybody explain this behaviour and what should I correct or what is the better way to do this. I'm using Express with ejs, I have no prior experience with ejs so I'm probably doing something wrong.

hhusnjak
  • 63
  • 5

1 Answers1

1

This may be many years old and have potential 'duplicates' regarding this type of thing but I stumbled upon this issue myself, and this is the first thread I found so I thought I might link other threads and useful information here. Hope that others (and my self), that might end up on this thread in the future, find it helpful.

General info and code relating to this topic

Refer to some other Stack Overflow questions (and answers) here and also here for some insight in how to do this. I would recommend checking the 'Tags' section of ejs' official site here, as well.

Fixing up this code specifically

I'm not an expert in ejs and in fact have started using it quite recently, but that being said, I'm pretty confident that what I say won't make me sound like a fool (I hope) :)

In the provided code, within the for loop in the html element tags, you'll need to use the <%= %> tags as ones you used (<% %>) do not produce/return an output. You also seem to be missing the tags in the h5 element:

<h5><%= someElement.title %></h5>

The following code (ignoring the in correct tag used) is calling the doSomeAction function and returning before the html is rendered (not the desired result):

<!--
ejs (before page load) calls 'doSomeAction'
which logs the elements' name os console: > Title 1
-->
<a href="#" onclick="<% doSomeAction(someElement) %>">Some Action</a>

<!--
since the tags used don't return a value and merely
execute code, you'll get the following html
-->
<a href="#" onclick="">Some Action</a>

<!--
however, it did return a value ('<%= %>' tag is used)
it would be this
-->
<a href="#" onclick="undefined">Some Action</a>
<!--
this is of course because your function doesn't have a return
so the result is 'undefined'
->>

Instead I'd suggest passing a key string (for dictionary lookup) or an index of the item to the function as passing the object in this way, while it can work to output the object into the html to call the function with it, won't be pretty and if the data changes then you'll have old data for that event. With the all of above and referenced links in mind, I would do the following:

<%
// Uses the index in-case data is updated
let doSomeAction = (eleIndex) => {
    console.log(someArray[eleIndex].title);
};
/*
// Alternatively, use the key if sorted in a dict
let doSomeAction = (eleKey) => {
    console.log(someArray[eleKey].title);
};
*/
// I use for..of to get the item and the index and
//  can also also be async by adding 'await' in front
for /*await*/ (let [index, someObj] of someArray.entries()) {
%>
  <div>
    <h5><%= someElement.title %></h5>
    <a href="#" onclick="doSomeAction(<%= index %>)">Some Action</a>
  </div>
<%
}
%>

Which will result in the following rendered html:

<a href="#" onclick="doSomeAction(0)"><h5>Some Action</h5></a>
<a href="#" onclick="doSomeAction(1)"><h5>Some Action</h5></a>

One thing to note as well, since the front-end dom is calling the function (and not being called when ejs pre-processes this), the function and the item array will need to exist in its scope (document) and not just in the ejs/express code b/c it can't call those directly.

Hope this has been helpful future me and others if you end up here o/

Sir Kamui
  • 62
  • 6