I am trying to build node.js + express application which consumes the data from SQL server database. But I have a problem in getting the response for my executeStatement() which is within app.get() router function. I expect my code to render an array object which I could simply use in my ejs template or view. As shown in the code below column.value is the main array of object that I want to use on the frontend.
PS: I am fairly new to the world of programming.Thank you for your help!
var express = require('express');
var tediousExpress = require('express4-tedious');
var app = express();
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
app.set('view engine', 'ejs')
app.set('views', './views')
var config = { 'contains my credentials' }
var connection = new Connection(config);
app.use(function (req, res, next) {
req.sql = tediousExpress(connection);
next();
});
app.get('/products', function (req, res ) {
/* I want to get column.value object to be rendered to my frontend
I already used res.json() here but it returns nothing*/
function executeStatement() {
request = new Request("select count * from table where ArticleId=
24588 for json path", function(err, data) {
if (err) {
console.log(err);
} else {
console.log('total rows fetched ') ;
}
connection.close();
});
request.on('row', function(columns) {
columns.forEach(function(column) {
if (column.value === null) {
console.log('NULL');
} else {
column.value ;
}
});
});
connection.execSql(request) ;
}
});
You need to render within your callback to row event:
app.get('/products', function (req, res ) {
// assuming you get the datas in column.value
request = new Request("select count * from table where ArticleId=24588 for json path", function(err, rowCount) {
if (err) {
console.log(err);
} else {
console.log('total rows fetched ') ;
}
connection.close();
}
request.on('row', function(columns) {
const datas = []; // <-- array to hold values
columns.forEach(function(column) {
if (column.value === null) {
console.log('NULL');
} else {
datas.push(column.value);
}
});
res.render('myView', { datas })
});
connection.execSql(request);
}
In your view, you can access the datas like:
<% datas.forEach(value => { %>
// do something like
// <p><%= value %></p>
<% }) %>
You are not rendering any data to be passed to the front-end, also you aren't returning the array from the function.
To neaten up your logic, you should have something like this:
app.get('/products', function(err, data) {
res.render('products', { arrData: executeStatement() }, function(err, data) {
// ...
})
})
executeStatement() { // ... }
Then in executeStatement() change column.value to return column.value
In your front-end template you can then iterate the data and output it as you wish:
<% arrData.forEach(value => { %>
// do something with each value
<% }) %>
Related
In an Express JS connected to a mySQL db, I am trying to get some data of an already defined route/ query:
// customers.model.js
CUSTOMERS.getAll = (result) => {
let query = "SELECT * FROM customers"
sql.query(query, (err, res) => {
if (err) {
console.log("error: ", err)
result(null, err)
return
}
result(null, res)
})
}
// customers.controller.js
// GET customers is a standalone route and should output all the customers when called.
const CUSTOMERS = require("../models/customers.model.js")
exports.findAll = (req, res) => {
return CUSTOMERS.getAll((err, data) => {
if (err)
res.status(500).send({
message: err.message ||
"Some error occurred while retrieving customers...",
})
else res.send(data)
})
}
In payments.controller.js I would firstly like to get all users so I can do something with the data:
// payments.controller.js
// GET payments is also a standalone route and should get the customers,
// do something with the data and output a calculation with the help of this data
const CUSTOMERS = require("../models/customers.model.js")
exports.calculateAll = (req, res) => {
const customers = CUSTOMERS.getAll((err, data) => {
console.log('this always has correct data', data)
if (err) return err
else return data
})
console.log('this is always undefined', customers)
...
res.send(whatEverCalculatedData)...
}
But that data here is always undefined.
What am I doing wrong in the above, and what's the correct way to call this route inside another route?
I know it has similarities with this question but I couldn't sort it out for my particular example.
It's due to your call which is asynchronous.
You must wait your data being ready before rendering the results.
Maybe you could to use Promises or async/await statements.
For example:
CUSTOMERS.getAll = async () => {
const query = "SELECT * FROM customers";
try {
return await sql.query(query);
} catch (e) {
console.log(`An error occurred while fetching customers: ${e.message}.`);
return null;
}
}
exports.calculateAll = async (req, res) => {
try {
const data = await CUSTOMERS.getAll();
res.send(whatEverCalculatedData);
} catch (e) {
res.send(`Something went wront: ${e.message}.`);
}
}
Im beginner on mongo & javascript, i made a approximate code may be to push all API JSON response on my collection db, so i should take the lead with this code to do something that looks like something rather than doing nothing :
The goal of this file is convert to a card all result of JSON api response, but before that i need to store this response
import { defineStore } from "pinia";
import axios from "axios";
import { MongoClient } from 'mongodb'
export const useCardStore = defineStore("cardStore", {
state: () => ({
cards: [],
}),
getters:{
allCards: (state) => state.cards
},
actions: {
async loadCards() {
try {
const response = await axios.get("https://api.adzuna.com/....");
this.cards = response;
const { data } = response;
response.data.results.forEach((item) => {
console.log(item);
return item.get().then(response => {
JSON.parse(response.body.text());
var MongoClient = require('mongodb').MongoClient;
var url = 'mongodb://localhost:27017/MyCollection';
MongoClient.connect(url, function(db) {
var myDB = db.db("JobSeeker");
var myobj = [item];
myDB.collection("cardsJobs").insertMany(myobj, function(res) {
console.log("Number of item inserted: " + res.insertedCount);
db.close();
});
})
});
});
}
catch (error) {
console.log(error);
}
},
},
});
//modify your code like this
var url = "API CALL HERE"
request(url, function(error, response, body){
if(!error && response.statusCode == 200){
var data = JSON.parse(body);
res.send(data);
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/mydb";
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var myobj = [];
myobj.push(data);
db.collection("dabas").insertMany(myobj, function(err, res) {
if (err) throw err;
console.log("Number of documents inserted: " + res.insertedCount);
db.close();
});
});
}
});
//your problem is you passing obj not array
//see below link for your reference
https://docs.mongodb.com/manual/reference/method/db.collection.insertMany/
I have data in my database (MongoDB) and I am finding data from DB and saving it to array. And when button is clicked on page I want to send that data to my JavaScript file and using DOM show it on page.
I am finding data form DB when page loads:
app.get('/', function(req, res) {
var ipsumTextArray = [];
Ipsum.find({}, function(err, allIpsumTexts) {
if (err) {
console.log(err);
} else {
allIpsumTexts.forEach(function(ipsum) {
ipsumTextArray.push(ipsum.text);
});
}
res.render('home');
});
});
And in my other JavaScript file I want this function to get data from DB and do whatever I want.
function randomIpsum(text) {
text.value = 'text from database'; // text is textarea where I want to show text
}
You need to render with a parameter.
app.get('/', function(req, res) {
var ipsumTextArray = [];
Ipsum.find({}, function(err, allIpsumTexts) {
if (err) {
console.log(err);
} else {
allIpsumTexts.forEach(function(ipsum) {
ipsumTextArray.push(ipsum.text);
});
}
res.render('home', { arr: ipsumTextArray });
});
});
In the front-end (view):
var arr= {{ arr }}
function randomIpsum(text) {
//text.value = 'text from database'; // text is textarea where I want to show text
text.value = arr[0]
}
OR
You can send a plain text from your nodejs.
app.get('/', function(req, res) {
var ipsumTextArray = [];
Ipsum.find({}, function(err, allIpsumTexts) {
if (err) {
console.log(err);
} else {
allIpsumTexts.forEach(function(ipsum) {
ipsumTextArray.push(ipsum.text);
});
}
res.send(ipsumTextArray);
});
});
You can get the data using jQuery in the front-end.
<button id="btn">Get Data</button>
$("#btn").on("click", function(){
$.get("/", function(data){
randomIpsum(text, data)
})
})
function randomIpsum(text, data) {
//text.value = 'text from database'; // text is textarea where I want to show text
text.value = data
}
I want to limit how many posts each category can display with a .limit() function and i am not sure how to come by this.
I am using Mongoose and Express.
My code so far is a follows.
router.get('/', function (req, res) {
MainArticle.find({ category: ['Worldwide', 'U.S. News'] }, function (err, mainArticles) {
if (err) {
console.log(err);
} else {
res.render('landing', { mainArticles: mainArticles });
}
});
});
If i was to output the results with EJS, it will display all the results of both categories. And if i was to limit, it would just limit to the integer i set.
I'm not sure what to pass on so i can display the two articles at different parts of the webpage as well as limit how many posts to show.
router.get('/profile', function (req, res) {
// Retrieve the desired count for each category (for example, through a query parameter) defaulting to some number as needed.
var limit = req.query.limit || 10;
// Create an object to hold the results
var result = {};
// Get data for the world wide category
MainArticle.find({
category: ['Worldwide'],
})
.limit(limit)
.exec(function (err, worldwideArticles) {
if (err) {
console.log(err);
} else {
// Add the worldwide data to the result set
result.worldwideArticles = worldwideArticles;
// Get data for the US news category
MainArticle.find({
category: ['U.S. News'],
})
.limit(limit)
.exec(function (err, usArticles) {
if (err) {
console.log(err);
} else {
result.usArticles = usArticles;
// Hand the two different sets separately to the template
// You will obviously have to change the template code to handle the new data structure of different categories
res.render('profile', { result: result });
}
});
}
});
});
EJS
<script type="text/javascript">
var json_data = <%= JSON.stringify( result ); %>
</script>
This displays articles for "Worldwide", limited to 10 articles.
<ul>
<% result.worldwideArticles.forEach(function(mainArticles){ %>
<li>
<div class="img-container">
<img src="<%= mainArticles.image %>" alt="">
<div class="title-container">
<%=mainArticles.title %>
</div>
</div>
<% }); %>
You could look at how I would do it here. Let me know if this works.
router.get('/', (req, res) => {
MainArticle
.where('category')
.in(['Worldwide', 'U.S. News'])
.limit(10)
.then(mainArticles => {
res.render('landing', { mainArticles })
})
.catch(err => {
console.log(err)
})
)
}
You may need to have a select here, but I cannot test it. If it doesn't work just add the properties you want to select after the .in. like .select('name', 'age', 'tags') or even better .select({}) but I don't know what will work without testing. I'm just going off documentation here:
http://mongoosejs.com/docs/2.7.x/docs/finding-documents.html
Reading the Query API another way to do this would be like this:
router.get('/', (req, res) => {
const fetchArticles = MainArticle.find({})
fetchArticles
.where('category')
.in(['Worldwide', 'U.S. News'])
.limit(10)
.then(mainArticles => {
res.render('landing', { mainArticles })
})
.catch(err => {
console.log(err)
})
)
}
.. or if you want to get fancy:
router.get('/', async (req, res) => {
function getArticles() {
return MainArticle
.where('category')
.in(['Worldwide', 'U.S. News'])
.limit(10)
.exec()
}
try {
let mainArticles = await getArticles()
res.render('landing', { mainArticles })
} catch (error) {
console.log(error)
}
}
UPDATE based on your last commit.
router.get('/', async (req, res) => {
function getWorldwideArticles() {
return worldwideArticles
.where('category')
.in(['Worldwide'])
.limit(10)
.exec()
}
function getUSArticles() {
return usArticles
.where('category')
.in(['U.S. News'])
.limit(10)
.exec()
}
try {
let worldwideArticles = await getWorldwideArticles()
let usArticles = await getUSArticles()
res.render('landing', { worldwideArticles, usArticles })
} catch (error) {
console.log(error)
}
}
I solved this by using what Chirag Ravindra posted and passed on
<script type="text/javascript">
var json_data = <%- JSON.stringify( result ); %>
</script>
above my forEach statements on my EJS because EJS can't call variables from clientside
Then i just used
<% result.worldwideArticles.forEach(function(mainArticles){ %>
<% }); %>
<% result.usArticles.forEach(function(mainArticles){ %>
<% }); %>
Where i wanted to post the two categories.
One way to do this is to query for each category separately and build your result for the template by setting the result of the two find operations on different keys in the result.
router.get('/', function(req, res) {
// Retrieve the desired count for each category (for example, through a query parameter) defaulting to some number as needed.
var limit = req.query.limit || 10;
// Create an object to hold the results
var result = {};
// Get data for the world wide category
MainArticle.find({
category: ['Worldwide']
})
.limit(limit)
.exec(function(err, worldwideArticles) {
if (err) {
console.log(err);
} else {
// Add the worldwide data to the result set
result.worldwideArticles = worldwideArticles;
// Get data for the US news category
MainArticle.find({
category: ['U.S. News']
})
.limit(limit)
.exec(function(err, usArticles) {
if (err) {
console.log(err);
} else {
result.usArticles = usArticles;
// Hand the two different sets separately to the template
// You will obviously have to change the template code to handle the new data structure of different categories
res.render('landing', result);
}
});
}
});
});
I just started to learn SailsJS and I ran into the following problem:
In the UserController I've got following method:
/**
* `UserController.create()`
*/
create: function (req, res) {
var d = {};
c = {'fullname' : 'Sergiu Pirlici'}
User.findOne(c, function(err, found) {
d.found = found;
});
User.destroy(c, function(err){
d.destroyed = !err;
});
if(req.method == 'GET') {
res.view(d);
}
else if(req.method == 'POST') {
var dat = req.params.all();
User.create(dat, function(err, created){
d.err = err;
if(!err) {
d.created = created;
}
});
}
d.foo = true;
res.json(d);
},
In variable d I want to collect data and pass it to view or send as JSON.
But in the value of d is {'foo': true}.
I understant that this happens because precedent actions are asynchronous.
Is there an another way to collect data and pass it to response?
How can I do this?
you can use a service http://sailsjs.org/#/documentation/concepts/Services
then pass a callback parameter.
so on the UserService.js
module.exports = {
FindOne: function(cb){
User.findOne(c, function(err, found) {
cb(found);
});
}
};
then on your controller:
UserService.FindOne(function(found){
//other logic
var d = { result : found } ;
res.json(d);
});