express icon indicating copy to clipboard operation
express copied to clipboard

Added local variables on render

Open JStyle21 opened this issue 3 years ago • 9 comments

Hi,

I'm using express and ejs. I passed an object to res.render then called a for in loop on it on the template but when it's printed it has _locals inside too. Everywhere i checked inside the object it wasn't there, yet on output to the end user it has this extra variable. I didn't find anything directly on this but a couple of other issues kinda point out that local variables are automatically included in the response to the view and yet i have never encountered this situation before.

I had to do a hacky solution for this with an if statement checking for this and ignoring it.

Any other comments on this?

JStyle21 avatar Oct 10 '20 22:10 JStyle21

There is nothing in express that is adding something called _locals that I am aware of. Can you provide a reproducible example of the issue you are having? Does it happen with all template engines or only ejs?

dougwilson avatar Oct 10 '20 23:10 dougwilson

That was a fast reply and an unexpected answer. Sure i"l try to provide anything i can. No i haven't used or tried anything but EJS.

Here is the code:

groupby(Function)

const groupBy = function (xs, key) {
    return xs.reduce(function (rv, x) {
        (rv[x[key]] = rv[x[key]] || []).push(x);
        return rv;
    }, {});
};

Route

app.get('/test', async (req, res) => {
t = await db.getQuestions(1);
zz = groupBy(t, 'question');
res.render('pages/test', zz);
});

View(EJS):

<% for( const property in zz ) { %>
<p><%= property %></p>
<% } %> 

As far as the Data, this is how it looks like after each step:

From DB

[
  {
    question_id: 1,
    question: 'Lorem ipsum dolor sit amet',
    answer: 'Lorem',
    value: 1
  },
  {
    question_id: 1,
    question: 'Lorem ipsum dolor sit amet',
    answer: 'ipsum',
    value: 3
  },
  {
    question_id: 1,
    question: 'Lorem ipsum dolor sit amet',
    answer: 'dolor',
    value: 5
  },
]

After groupBy:

{
  'Lorem ipsum dolor sit amet': [
    {
      question_id: 1,
      question: 'Lorem ipsum dolor sit amet',
      answer: 'Lorem',
      value: 1
    },
    {
      question_id: 1,
      question: 'Lorem ipsum dolor sit amet',
      answer: 'ipsum',
      value: 3
    },
    {
      question_id: 1,
      question: 'Lorem ipsum dolor sit amet',
      answer: 'dolor',
      value: 5
    }
  ],
}

In the FE:

<p>Lorem ipsum dolor sit amet</p>
            
<p>_locals</p>

JStyle21 avatar Oct 10 '20 23:10 JStyle21

BTW if i stringify the object on render res.render('pages/test', JSON.stringify(zz));

I get the following error: (node:337233) UnhandledPromiseRejectionWarning: TypeError: Cannot create property '_locals' on string

So i'm thinking render adds that property on any object it sends.

Update: Just tried putting that object in an object when rendering and that made the problem go away res.render('pages/test', {zz});

Very strange but somehow related to passing an object directly to render rather than inside an object. Never had a problem before...

JStyle21 avatar Oct 11 '20 10:10 JStyle21

Here is the link from Express Documentation regarding res.render. You should pass variables to views as an object. Regarding the _locals, it is still not obvious to me how to reproduce the bug, seems like you are calling the _locals yourself in the view.

mrmammadov avatar Dec 29 '20 13:12 mrmammadov

@mrmammadov What do you mean i'm calling it in the view? where do you see it in my example code? I've only seen it on the end client and not anywhere before that so no idea how it got there

JStyle21 avatar Dec 29 '20 21:12 JStyle21

I am sorry, it was a misunderstanding. Although I still cannot reproduce your issue, passing the variable as an object in render function will give you desired outcome.

mrmammadov avatar Dec 29 '20 22:12 mrmammadov

I am sorry, it was a misunderstanding. Although I still cannot reproduce your issue, passing the variable as an object in render function will give you desired outcome.

No worries, at first i also thought the same however i somehow broke it.

It's been a while but i still have that code somewhere, let me see if i can reproduce it myself and then i"ll make a new repo so you can test it too.

If i fail at reproducing then i"ll come back to close the issue.

JStyle21 avatar Dec 30 '20 00:12 JStyle21

Hi,

Sorry it took so long to reply back I was able to recreate this at the end, here is a demo as promised: https://github.com/JStyle21/testing

JStyle21 avatar Jan 31 '21 15:01 JStyle21

The _locals key is added to res.render(view, options) options here: https://github.com/expressjs/express/blob/508936853a6e311099c9985d4c11a4b1b8f6af07/lib/response.js#L1001-L1004 You are passing an object literal that is treated as options, to prevent this from happening you need to specify a key for it as you already suggested here. The right code is definitely this:

app.get('/', (req, res) => {
    const zz = { 1: 1, 2: 2, 3: 3 };
    return res.render('reg', { zz });
});

fedeci avatar Jan 31 '21 16:01 fedeci

@JStyle21 I'm closing this issue as @fedeci has given you the solution. Feel free to get back if the issue is still present and I'll reopen this issue.

aravindvnair99 avatar Mar 27 '23 07:03 aravindvnair99