How do I rendering views based on multiple queries in Express? - node.js

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:
Find below my 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

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.

Related

Form Select onChange Sends Data/Info to Query on Server

I'm using Nodejs, Express, and EJS.
Here's what works...
I can use an unordered list of hyperlinks and send the info/variable via req.params this way...
db.ejs code
<ul>
<% dbTitle.forEach(function(dbTitle){ %>
<li><%= dbTitle.dbTitle %></li>
<% }) %>
</ul>
server.js code
app.get('/db/:dbTitle', async (req, res) => {
const {dbTitle} = req.params;
console.log(dbTitle);
try {
const tabTitleResult = await session.run(`MATCH (db:Database {Title: $dbTitle})-->(t:Table)-->(tv:TableVersion)
Where NOT (tv)<--(:InformationAsset)
RETURN db.Title as dbTitle, tv.Title as tabTitle Order By db.Title, tv.Title ASC`, {dbTitle});
const tabTitleArr = tabTitleResult.records.map(({_fields}) => {
return {dbTitle:_fields[0],tabTitle:_fields[1]};
});
res.render('table.ejs', { tabTitle: tabTitleArr});
//console.log(tabTitleArr)
} catch(e) {
console.log("Something went wrong", e)
}
});
everything from above displays nicely on this page...
table.ejs code
<table>
<tr>
<th>Database-Title</th>
<th>Table-Title</th>
</tr>
<% tabTitle.forEach(function (tabTitle){ %>
<tr>
<td><%= tabTitle.dbTitle %></td>
<td><%= tabTitle.tabTitle %></td>
<% }) %>
</tr>
</table>
Here's what doesn't work...
Instead of an unordered list of hyperlinks, I would prefer to have a dropdown select, however my code doesn't work when I try to use a form select option method to send the info/variable via req.body...
db.ejs code
<form method="post" action="/db">
<label>Database Name</label><br>
<select name="dbTitle" onchange="this.form.submit();">
<option selected disabled> -- select an option --
<% dbTitle.forEach(function(dbTitle){ %>
<option name="dbTitle" value="<%= dbTitle.dbTitle %>"><%= dbTitle.dbTitle %></option>
<% }) %>
</option>
</select>
</form>
(Note: I am aware of how strange the nested options seem, this is required to force the --select an option-- option to appear first, removing the nesting with only the one option with data does not help.
Also, you'll note that I'm adding name="dbTitle" on more than one element in a desperate attempt to make something work, I believe it should only be on the select element.
Last, I'm also trying to send any info/variable via value="<%= dbTitle.dbTitle %>.)
server.js code
app.post('/db/:dbTitle', async (req, res) => {
const {dbTitle} = req.body;
console.log(dbTitle);
try {
const tabTitleResult = await session.run(`MATCH (db:Database {Title: $dbTitle})-->(t:Table)-->(tv:TableVersion)
Where NOT (tv)<--(:InformationAsset)
RETURN db.Title as dbTitle, tv.Title as tabTitle Order By db.Title, tv.Title ASC`, {dbTitle});
const tabTitleArr = tabTitleResult.records.map(({_fields}) => {
return {dbTitle:_fields[0],tabTitle:_fields[1]};
});
res.render('table.ejs', { tabTitle: tabTitleArr});
//console.log(tabTitleArr)
} catch(e) {
console.log("Something went wrong", e)
}
});
From here, when I run and then select from the dropdown, I receive an error of Cannot POST /table, and nothing shows in my console.log(dbTitle);, so I'm assuming no variable is being sent from my form to the server.
From what I've gathered in using a form vs ul li hyperlinks, there are some differences where the form needs to have method="post", and the server needs to be app.post with req.body instead of req.params. Or maybe this is incorrect?
Thank you for any help you can share.
I figured it out, here's what I needed to do.
Everything was fine on my client db.ejs.
In my server.js, I needed to change app.post('/auradbtable/:dbTitle' to app.post('/auradbtable?:dbTitle'... change the '/' to '?'.
And using const {dbTitle}=req.body; is correct.

Nodejs and Handlebars - list duplicating on refresh, instead of refreshing

I've been playing around with Handlebars templating. Current practice is a simple todo list. On first load it's just fine, but if I refresh my page, it doesn't actually refresh. Instead, it duplicates the results of my GET and appends them to the list.
So, first time the list is like:
Get groceries
Take kids to soccer
Then upon refresh I get:
Get groceries
Take kids to soccer
Get groceries
Take kids to soccer
index.js GET method
app.get('/', (req, res) => {
let query = "select * from todos";
db.query(query, function(err, rows) {
if (err) throw err;
rows.forEach(function(todo) {
todos.push(todo.todo);
console.log(todo.todo)
})
res.render('index', {
todos
});
})
});
index.hbs
<h2>Here's some Todos</h2>
<ul id="list">
{{#each todos}}
<li>{{this}}</li>
{{/each}}
</ul>
It seems todos is a global variable, or valid outside of the for loop scope. For each query it adds the existing db items in todos. Before calling the loop make sure the todos is an empty list:
if (err) throw err;
todos = [];
rows.forEach(function(todo) {
todos.push(todo.todo);
console.log(todo.todo)
})

Multiple mongoDB queries in one router.get method nodejs

I would like to have multiple queries in a single router method as follows in my index.js file,
router.get('/users', function(req, res) {
var db = req.db;
var users = db.get('users');
var col_name=req.query.colname;
var col_value=req.query.colvalue;
var query={};
query[col_name]=col_value;
console.log(col_name);
console.log(col_value);
console.log(query);
//using this i would like to populate my dropdown list
users.distinct('symbol',{limit: 10000},function(e, syms){
res.send('users', {
title: 'usersSym',
'usersSym': syms
});
});
// using this I would populate a table in html
users.find(query,{limit: 10000},function(e, docs){
res.render('users', {
title: 'Users',
'users': docs
});
});
});
And in my .ejs file I'm trying to do the following :
<html>
<head>
//drop down list populating with first query
<select id="selected" name="colvalue" >
<option value="">--Select--</option>
<% usersSym.forEach(function(usersym) { %>
<option value="<%= usersym.symbol %>"><%= usersym.symbol %></option>
<% }); %>
</select>
//Table populating with second query
<table >
<tr >
<th>Symbol</th>
<th>Order_id</th>
</tr>
<tr>
<% users.forEach(function(user) { %>
<td ><%= user.symbol %></td>
<td><%= user.order_id %></td>
</tr>
<% }); %>
</table>
</body>
</head>
</html>
But no luck. Wondering whether I'm going in right direction or not. If not please guide me in right way.
// 1st fetch symbol
users.distinct('symbol',{limit: 10000},function(e, syms){
// 2nd fetch [users]
users.find(query,{limit: 10000},function(e, docs){
res.render('users',
{
usersSym: syms,
users: docs
}
);
});
});
// P.S. do not forget to check on error on each callback
You can only send data back to the client once. One way would be to do all those DB queries in a sequence, and then send all the data. Another might be to do it your way, check the status of all DB queries, and if all are done, then send the data.

Using Ember Data to load data async from node sever

My Model :
App.Contacts = DS.Model.extend({
name : DS.attr('string'),
number : DS.attr('number')
});
This is how i save a record :
App.AddController = Ember.Controller.extend({
actions : {
addContact : function(){
var post = this.store.createRecord('Contacts',{
name : this.get('name') ,
number : this.get('number')
});
post.save();
}
}
});
Acc to Ember's offical guide, this would send a POST request to /Contacts , so to handle it, i used this in nodejs/expressjs
app.post('/contacts',function(req,res){
posts.push( req.body);
console.log(posts);
res.send({status: 'OK'});
});
Now i wish to retrieve it, into another template called all so i used :
App.AllRoute = Ember.Route.extend({
model : function(){
return this.store.find('Contacts');
},
setupController : function(controller,model){
controller.set('contactList',model);
}
});
Acc to Emberjs guides, model hook supports promises out-of-the-box . so i assumed this should work.
My template :
<script type="text/x-handlebars" id="all" >
Hello
<table>
{{#each contact in contactList}}
<tr>
<td>{{contact.name}} </td>
<td>{{contact.number}} </td>
</tr>
{{else}}
<tr><td>No contacts yet </td> </tr>
{{/each}}
</table>
</script>
Question
But the model returns nothing, i understand that this.store.find('Contacts') doesn't return a javascript array, but essentially and object , implimenting Ember.Enumerable
But on the server side, the posts is an javascript array, therefore there might be an type mismatch between then. how to resolve this?
EDIT:
To avoid any confusions in client side Ember code , This works properly, so there is some problem with the round trip to server.
App.AllRoute = Ember.Route.extend({
model : function(){
return this.store.all('Contacts');
},
setupController : function(controller,model){
controller.set('contactList',model);
}
});
If you could provide a jsfiddle, it be nice. I'm not sure whether contactList is defined or not and whether the alias for that controller is actually defined. So based on what I see, I think the problem is you're iterating over a controller that does not have the model properly defined.
I'd suggest trying to do:
{{#each}}
<tr>
<td>{{contact.name}} </td>
<td>{{contact.number}} </td>
</tr>
{{else}}
<tr><td>No contacts yet </td> </tr>
{{/each}}
If you really want to use the contactList controller, then you need to make sure that the App.AllController "needs" the contactListController.
App.ContactListController = Ember.ArrayController.extend({}) // needs to be an array controller
App.AddController = Ember.ArrayController.extend({
needs: ["contactList"], // controller's used within this controller
contactList: Ember.computed.alias("controllers.contactList"), // needed to iterate over the model how you're doing it.
Both of these solutions should work assuming your data is actually loaded in Ember Data. You might want to check the "data" tab on the ember-data console. If you don't have that browser extension installed, do it. It's incredibly useful.
If all else fails, try logging to verify expected values using {{log contactList}}
Good luck

Unable to get Meteor-Pagination to work

I am trying to add pagination support to my meteor app but my template shows a blank page as soon as I add the {{{pagination}}} tag in my template. There are no errors in the log.
My client js (routing info) looks like this
Meteor.Router.add({
'/': function () {
var user;
if (Meteor.loggingIn()) {
console.log('home: loading');
return 'loading';
}
user = Meteor.user();
if (!user) {
console.log('homer: signin');
return 'user_signin';
}
// start on 'start' page
console.log('home: start');
return 'page';
},
'/landing': 'landing',
'*': 'not_found',
'/landing/:page': function (page) {
Session.set('page', page) ;
return 'landing' ;
}
});
My Landing.js looks like this
Template.userList.pagination = function () {
return Pagination.links('/landing', Meteor.users.find({}).count(), {currentPage: Session.get('page'), perPage: 8}) ;
}
My landing template is as follows:
</thead>
<tbody>
{{#each users}}
{{> user}}
{{/each}}
{{{pagination}}}
</tbody>
</table>
I see a couple things with the code posted.
I don't know much about the Pagination addin but it looks like you have it inside the table tag and according to this (https://github.com/egtann/meteor-pagination) it renders a div. I believe that would be invalid.
In your routes you have the wildcard '*' before the 'landing/:page'. I believe it would match that one first. Should have the '*' be the last route you add.

Resources