2

My question is how to make sure my variables get loaded before the page gets rendered ? I use node.js with express and everyauth.

This is the code:

app.get('/', function(req, res) {
   app.locals.rep = global.repos;
   res.render('index'); 
});

Currently, the page gets loaded, but the paragraph containing the variable is empty and I get an error: "Cannot read property '...' of undefined [...]". As I refresh the page, the content flows in.

index.jade

extends layout
block content
  h1= title
  - if (!everyauth.loggedIn)
    a(href="/auth/github") Login with github
  - else
    h3
      a(href='/logout') Logout
      h3 GitHub User Data
      - each r in rep
        p= r.name
        p= r.description

This is where I set the repos variable:

var repos;
global.repos = [];

everyauth.github
.appId(config.gh_clientId)
.appSecret(config.gh_secret)
.findOrCreateUser( function (sess, accessToken, accessTokenExtra, ghUser) {

if (typeof usersByGhId[ghUser.id] === 'undefined') {

  usersByGhId[ghUser.id] = addUser('github', ghUser);

  var options = { 
            host: "api.github.com", 
            path: "/users/cmarius02/repos",
            method: "GET",
            headers: {
               "User-Agent": "github-connect" 
            }
  };

  var request= https.request(options, function(response){
    var body='';
    response.on("data", function(chunk){
      body+=chunk.toString("utf8");
    });

    response.on("end", function(){
      var json=JSON.parse(body);

      // console.log(json);  

      global.repos = [];
      for (var k in json)
        if ({}.hasOwnProperty.call(json, k)) {
          global.repos.push({
            name: json[k].name,
            description: json[k].description
          });
        }
    });
  });
  request.end();
  return usersByGhId[ghUser.id];

} else {
  return usersByGhId[ghUser.id];
}   })

.redirectPath('/');

It's my first day of node.js so please be pacient. Thank you in advance.

4
  • What is global.repos? Show where it's defined. You would also typically set app.locals when you configure the server before startup. Commented Jul 2, 2013 at 19:28
  • Could you also post your view? Commented Jul 2, 2013 at 19:34
  • If I were you, I would add some logging throughout the GitHub API call and then see if it was being called before or after the first request to your server. That should help you determine where the issue is taking place. Report back with your findings and I'll try to help further :) Are you sure that the load repos function is being called on first load of the server? If no one is logged in? Commented Jul 2, 2013 at 20:12
  • The GitHub API request is done only when the login button is pressed, if that is what you were asking. A console.log() confirmed that. Commented Jul 3, 2013 at 9:07

2 Answers 2

1

I use passport instead of everyauth, but I think the approach should be similar

Express uses middleware to chain up code, and each middleware is responsable of calling the next one (even if they dont know which is it)

I do something like this:

    app.all ('*', function (req,res,next) {
      // Add the user from the session
      if req.user?
          res.locals.session_user = req.user
      // Add the flash messages
      res.locals.flash_messages = req.flash()

      next()
    });

    app.get('/', function(req, res) {
      res.render('index'); 
    });

The app.all means that the route will match get, put and post request. The * is a regexp that matches all routes, so the first callback will be called before all your routes

req.user is filled by passport, I imagine everyauth has something similar.

res.locals is the one that allows you to pass information to the view. Then, in the view, you can just reference the element as this.session_user (I dont use jade, but you get the point).

Also note that with the same approach, I pass flashes between requests to the view.

The order is important!. And next() assures you that your actual route wont be handled until the previous one hasnt resolved. If you need, you can call next inside another callback in that method.

Sign up to request clarification or add additional context in comments.

5 Comments

It reacts in the same way. It looks like it does the variable setting and rendering in parallel. Isn't there any way to bound the two commands ?
I have also added 'while (app.locals.z != 1) { app.locals.rep = global.repos; app.locals.z = 1; } ' before 'res.render(...)', but still no result.
Ive read around that everyauth has the options to use promises... The aproach I gave you is kind of syncronic. The second route wont be called until the firstone calls next(), but maybe req.user is not an object but the promise to an object. Check what the value is doing console.log typeof req.user and things alike. For the promises, here is a link stackoverflow.com/questions/8761230/whats-everyauth-promise
Thx Hernan. But I have decided to switch to using passport. Can I have a link to your project ?
Sadly Marius, its not an open source project, but I extracted the necesary parts with some hints of how I order my code in this gist (dunno if good or bad, but it works for me). Have any doubt, just ask :) gist.github.com/hrajchert/5935329
0

By using Express middleware, you can override render by doing:

  app.use(function(req, res, next) {
    // Grap render
    var _render = res.render;

    // Override logic
    res.render = function(view, options, cb) {
      // Add logic (Example)
      if (!options.foo) {
        options.bar = true
      }

      // Original call
      _render.call(this, view, options, cb)
    }
    next()
  })

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.