SirBT
SirBT

Reputation: 1698

How do I rendering views based on multiple queries in Express?

I have a collection with various data that I would like to use when rendering my ejs page. I only seem to be able to render contents from ONE query and not other queries.

Kindly help me figure out how to render the contents from other queries.

Find below some of the contents of my collection: contents within my collection

Find below my rendered ejs page the rendered ejs page

Pay special attention to the Unidentified in the Transactions from: Unidentified county sentence.

Now... lets take a look at the code that generates this page:

function county (req, res) {
transModel.findOne({ transCounty : /Nairobi/i,
                        })
                        .limit(5)
                        .select({ transCounty:1})
                        .exec( (err, result)=> {
                            if (err) throw err;
                                console.log('>> ' +result); 
                                console.log('We in county function!');
                                return result;
                            });    
};

app.get('/list', async (req,res)=> {

console.log('## ' +county());
transModel.find({transIndustry: 'Pharmacy'}, (err, docs)=> {
if (!err) 
    {
        res.render('list',  {data : docs, countyName: county}); 
    }
else
    {
//          res.status(status).send(body);      
    }
        
})
});

In the console, the code above logs:

## undefined
>> { _id: 609f7ed8fe1fd8b3193b0b77, transCounty: 'Nairobi' }
We in county function!

And now find below the ejs file contents:

<body>
    <h1> 
        Transactions
    </h1>

    <p> Transactions from: <b> <%= countyName.transCounty %> </b> County: </p>

    <table>
        <tr>
            <th> _id  </th>
            <th> Transaction Amount </th>       
            <th> Transaction Industry </th> 
        </tr>
        <% data.forEach(function(entry) {%>
        <tr>
            <td> <%=entry._id%> </td> 
            <td> <%=entry.transAmount%> </td>               
            <td> <%=entry.transIndustry%> </td>     
        </tr>
        <%});%>
        
    </table>

Kindly help me understand where I am going wrong and how I can get the <%= countyName.transCounty %> to display Nairobi in my rendered ejs file

Upvotes: 0

Views: 95

Answers (1)

ippi
ippi

Reputation: 10167

The main difference between docs and county is that docs is the results from a database-query (ok!), while county is a function which you only reference to and never run (problem 1), but if you were to run it still returns nothing (problem 2), but it does run a query, so you have that going for you!

so, not the complete solution, but just to point you in the right direction:

  • You need to call county at some point using county() with parenthesises.
  • Since the nature of database queries is that they are asynchronous you need to either use a callback pattern or a promise-based solution.

A callback solution could look like:

   // Here we are calling the function, and the entire function-argument is the callback.
   county((countyResult /* the callback will be called with result in the future */) => {
        // wrap your existing code, 
        // because the result will only be available inside the callback
        transModel.find({transIndustry: 'Pharmacy'}, (err, docs) => {
            ...
            res.render('list', {data : docs, countyName: countyResult});
        });
   });

And the called function could look something like this:

function county (callback /* accept a callback */) {
    transModel.findOne({ transCounty : /Nairobi/i })
        .limit(5)
        .select({ transCounty:1})
        .exec((err, result) => {
            if (err) throw err;
            callback(result);  // Use the callback when you have the result.
        });    
}

Because the need to wrap callbacks the result never becomes very pretty.

Instead you could use promises. for example:

let countyResult = await county();
let docs = await transModel.find({transIndustry: 'Pharmacy'}).exec();
res.render('list', {data : docs, countyName: countyResult});

And the called function could look something like this:

function county (req, res) {
    return transModel
        .findOne({ transCounty : /Nairobi/i })
        .limit(5)
        .select({ transCounty: 1})
        .exec();  // If you use exec without a callback it'll return a promise.
};

This is much cleaner but error handling needs to be done using try/catch. For example:

let countyResult;
try {
     countyResult = await county();
catch(err) {
    throw err; 
    // Of course, if all you do is throw, then you don't even need the `try/catch`.
    // Try/catch is to catch thrown errors and handle them.
} 

You still might want to check that the result contains anything. A query with no data found is not an error.

Disclaimer: The code is for describing the flow. I've tried to get it right, but nothing here is tested, so there is bound to be errors.

Upvotes: 1

Related Questions