Currently I have axios and cheerio return data from a webpage. I then setup express to setup a few views. I double checked my index.hbs and it include {{data}} inside the body. This should allow the page to render the text from the index render data: dealss . Am I missing anything ? The dealss obj holds 4 different objects that I can access.
getdeals(result => console.log(result.totaldeals[0].date))
This returns [ 09/04/2019/ ] in the console.
const path = require('path')
const express = require('express')
const hbs = require('hbs')
const axios = require('axios');
const cheerio = require('cheerio');
const app = express()
// Define paths for express config
const publicDirPath = path.join(__dirname, '../public')
const viewsPath = path.join(__dirname, '../templates/views')
const partialsPath = path.join(__dirname, '../templates/partials')
// Setup handlebars engine and views location
app.set('view engine', 'hbs')
app.set('views', viewsPath)
hbs.registerPartials(partialsPath)
// Setup static directory to serve
app.use(express.static(publicDirPath))
// Views
app.get('', (req, res) => {
res.render('index', {
title: 'ClearVision',
data: dealss,
name: 'Chris'
})
})
app.get('/about', (req, res) => {
res.render('about', {
title: 'ClearVision - About Us',
header: 'About Us',
name: 'Chris'
})
})
app.get('/help', (req, res) => {
res.render('help', {
title: 'ClearVision - Help',
helptext: 'Please contact x for help.',
name: 'Chris'
})
})
app.get('/weather', (req, res) => {
res.send({
forecast: 'It is sunny.',
location: 'x, Ca'
})
})
app.listen(1337, () => {
console.log('Server is currently running on port 1337.')
})
const url = 'https://abcdef.com/';
axios.defaults.withCredentials = true
// Get the deals
const getdeals = (callback) => {
axios(url, {
headers: {
Cookie: "x=xx;"
}
})
.then(response => {
const html = response.data;
const $ = cheerio.load(html);
// Deals Page
const statsTable = $('tbody > tr');
const totaldeals = [];
// Loop Table for data in each row
statsTable.each(function () {
const nwline = "\n"
let date = $(this).find('td:nth-child(1)').text()
let bodydeals = $(this).find('td:nth-child(2)').text()
let newdeal = $(this).find('td:nth-child(3)').text()
let revdeal = $(this).find('td:nth-child(4)').text()
let monthlydealrev = $(this).find('td:nth-child(5)').text()
// Clear /n
if (date.includes(nwline)) {
date = date.toString().replace("\n", ""),
date = date.toString().replace("\n", "")
}
// Clear /n
if (bodydeals.includes(nwline)) {
bodydeals = bodydeals.toString().replace("\n", ""),
bodydeals = bodydeals.toString().replace("\n", ""),
bodydeals = bodydeals.toString().replace("\n", "")
}
// Clear /n
if (newdeal.includes(nwline)) {
newdeal = newdeal.toString().replace("\n", ""),
newdeal = newdeal.toString().replace("\n", ""),
newdeal = newdeal.toString().replace("\n", "")
}
// Clear /n
if (revdeal.includes(nwline)) {
revdeal = revdeal.toString().replace("\n", ""),
revdeal = revdeal.toString().replace("\n", ""),
revdeal = revdeal.toString().replace("\n", "")
}
// Clear /n (lookup jquery table functions)
if (monthlydealrev.includes(nwline)) {
monthlydealrev = monthlydealrev.toString().replace("\n", ""),
monthlydealrev = monthlydealrev.toString().replace("\n", ""),
monthlydealrev = monthlydealrev.toString().replace("\n", "")
}
totaldeals.push({
date,
bodydeals,
newdeal,
revdeal,
monthlydealrev
})
})
callback({
totaldeals
})
//console.log(totaldeals[1].date)
})
.catch(console.error);
}
function newFunction() {[getdeals(result => console.log(result.totaldeals))]}
I added a data: dealss under the res.render for the index. I also checked the index.hbs which has {{data}} in there. Shouldn't this just add the text to the screen?
Any ideas on how to print it to the view?
You just need to pass it as a variable to your hbs file:
app.get('', (req, res) => {
getdeals(result => {
res.render('index', {
title: 'ClearVision',
data: result, // or result.totaldeals depending
name: 'Chris' // on what you really mean
})
});
})
Improvements
If you rewrite getdeals() to return a Promise instead of accepting a callback you can use async/await:
const getdeals = () => {
// NOTE THIS CHANGE, return axios promise:
return axios(url, {
/* ... */
})
.then(response => {
/* .. */
statsTable.each(function () {
/* .. */
})
return totaldeals; // NOTE we return the result instead
// of calling a callback. This will
// return a resolved Promise
})
// Don't catch here, your request will hang if an error occurs
}
Now with the change above (return axios and return the result) we can rewrite the route as:
app.get('', async (req, res, next) => { // must have async keyword!
try {
let result = await getdeals();
res.render('index', {
title: 'ClearVision',
data: result, // or result.totaldeals depending
name: 'Chris' // on what you really mean
})
}
catch (err) {
console.log(err);
next(err); // this will close the request socket
}
})
Related
I'm trying to send through multiple variables into a res.render but I'm unsure of how to do it without an undefined error. Either one or the other will have an object to pass through but never both at the same time. Here's my current code that will give an undefined error.
app.get('/', async (req, res) => {
if (req.query.search) {
const search = req.query.search
const regex = new RegExp(search, 'i')
const searchedblogposts = await BlogPost.find({title: {$regex: regex}})
console.log(searchedblogposts)
} else {
const blogposts = await BlogPost.find({})
}
res.render('index', {
blogposts,
searchedblogposts
})
})
Well, this is because one of blogPosts or searchBlogPosts remains undefined at any point of time and you're passing both in res.render . What you can do is this:
app.get('/', async (req, res) => {
let blogPosts;
if (req.query.search) {
const search = req.query.search
const regex = new RegExp(search, 'i')
blogPosts = await BlogPost.find({title: {$regex: regex}})
} else {
blogposts = await BlogPost.find({})
}
res.render('index', {
blogposts,
})
})
OR this:
app.get('/', async (req, res) => {
const blogPosts = req.query.search? await BlogPost.find({title: {$regex: new RegExp(req.query.search, 'i')}})
: await BlogPost.find({})
res.render('index', {
blogposts,
})
})
I have a project in Node JS in which I have a form to add new users.
How can I view this information in JSON format?
These are the data that I see:
name age country city
------------------------------
user1 22 Spain Madrid button{View JSON}
When I press the 'View JSON' button, the following must be displayed below the table:
[
"id": 1,
"name": "user1",
"age": 22,
"country": "Spain" {
"city":"Madrid"
}
]
My problem: how can I create a function that performs this conversion? How do I call the function from index.ejs?
I cleared and merged the codes. And I created a new endpoint as /export to export the data as CSV file. I couldn't test it so let me know if it doesn't work.
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const MongoClient = require('mongodb').MongoClient;
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(express.static('public'));
app.set('views', './src/views');
app.get('/', async (req, res) => {
const db = await mongoDB();
const person = await db.collection('person').find().toArray();
res.render('index.ejs', { person: person })
})
app.get('/export', async (req, res) => {
await convertCSV();
res.status(200).send( { success: 1 });
})
app.post('/person', async (req, res) => {
res.redirect('/');
})
app.listen(process.env.PORT, function () {
console.log(`server: http://${process.env.HOST}:${process.env.PORT}`);
})
const mongoDB = () => {
return new Promise((resolve, reject) => {
const url = 'mongodb://127.0.0.1:27017';
MongoClient.connect(url, { useUnifiedTopology: true })
.then(client => {
const db = client.db('users')
resolve(db);
})
.catch(error => reject(error))
});
}
const convertCSV = () => {
return new Promise((resolve, reject) => {
const converter = require("json-2-csv");
const fetch = require("node-fetch");
const fs = require("fs");
const flatten = require('flat');
const maxRecords = 10;
const getJson = async () => {
const response = await fetch(`http://${process.env.HOST}:${process.env.PORT}/users.json`);
const responseJson = await response.json();
return responseJson;
};
const convertToCSV = async () => {
const json = await getJson();
let keys = Object.keys(flatten(json[0]));
let options = {
keys: keys
};
converter.json2csv(json, json2csvCallback, options);
};
let json2csvCallback = function (err, csv) {
if (err) throw err;
const headers = csv.split('\n').slice(0, 1);
const records = csv.split('\n').slice(0,);
for (let i = 1; i < records.length; i = i + maxRecords) {
let dataOut = headers.concat(records.slice(i, i + 3)).join('\n');
let id = Math.floor(i / maxRecords) + 1;
fs.writeFileSync('data' + id + '.csv', dataOut)
}
};
await convertToCSV();
resolve();
})
}
However, it is not a good practice at all to using controller, index and route in the same file. A better approach would be to create routes, controllers folders and put the codes in a more orderly form.
Something like this (You can find better ones of course mine is just advice):
- index.js
- router.js (A router to manage your endpoints)
- controllers (Controller when you call the endpoint)
-> export.controller.js
-> person.controller.js
- routes (Endpoints)
-> export.route.js
-> person.route.js
- helpers
-> databaseHandler.js (Database connection handler)
When i call Create Option api it is working fine but when i call list api get error: Cannot read property 'count' of undefined Express (Node + MongoDB) API.here is my Option Controller File code.
i have Log DB.ProductDoption ,getting result but count function not working.
const _ = require('lodash');
const Joi = require('joi');
exports.create = async (req, res, next) => {
try {
const validateSchema = Joi.object().keys({
name: Joi.string().required(),
key: Joi.string().required(),
description: Joi.string().allow(['', null]).optional(),
options: Joi.array().items(Joi.object().keys({
key: Joi.string().required(),
displayText: Joi.string().required()
})).required()
});
const validate = Joi.validate(req.body, validateSchema);
if (validate.error) {
return next(PopulateResponse.validationError(validate.error));
}
const key = Helper.String.createAlias(req.body.key);
console.log(DB.ProductDoption);
const count = await DB.ProductDoption.count({ key });
if (count || validate.value.key === '_custom') {
return next(PopulateResponse.error({
message: 'Please add unique name for key'
}));
}
const option = new DB.ProductDoption(validate.value);
await option.save();
res.locals.option = option;
return next();
} catch (e) {
return next(e);
}
};
exports.list = async (req, res, next) => {
const page = Math.max(0, req.query.page - 1) || 0; // using a zero-based page index for use with skip()
const take = parseInt(req.query.take, 10) || 10;
try {
const query = Helper.App.populateDbQuery(req.query, {
text: ['name', 'key', 'description']
});
const sort = Helper.App.populateDBSort(req.query);
const count = await DB.ProductDoption.count(query);
const items = await DB.ProductDoption.find(query)
.collation({ locale: 'en' })
.sort(sort).skip(page * take)
.limit(take)
.exec();
res.locals.optionList = {
count,
items
};
next();
} catch (e) {
next(e);
}
};
collection.count is deprecated, and will be removed in a future version. Use Collection.countDocuments or Collection.estimatedDocumentCount instead
I'm aware this question has been asked before. I tried all the solutions that were provided but still I'm stuck. Please have a look at my code:
1) ReviewController.js
exports.setTourUserIds = (req, res, next) => {
// allow nested route
if (!req.body.tour) req.body.tour = req.params.tourId
if (!req.body.user) req.body.user = req.user.id
next()
}
exports.createReview = handlerFactory.createOne(Review)
exports.updateReview = handlerFactory.updateOne(Review)
exports.deleteReview = handlerFactory.deleteOne(Review)
2) ReviewRoute.js
let express = require('express')
let Router = express.Router({ mergeParams: true })
let reviewController = require('../controllers/reviewController')
let authController = require('../controllers/authController')
Router
.route('/')
.get(reviewController.getAllReviews)
.post(authController.protect,
authController.restrictTo('user'),
reviewController.setTourUserIds,
reviewController.createReview)
Router
.route('/:id')
.get(authController.protect, reviewController.getReview)
.patch(reviewController.updateReview)
.delete(reviewController.deleteReview)
module.exports = Router
3) handlerFactory.js
exports.createOne = Model => catchAsync(async (req, res, next) => {
let doc = await Model.createOne(req.body)
if (!doc) {
return next(new AppError('No document found with that ID', 404))
}
res.status(201).json({
status: 'success!',
data: { doc }
})
})
Please change your code from
exports.createReview = handlerFactory.createOne(Review)
exports.updateReview = handlerFactory.updateOne(Review)
exports.deleteReview = handlerFactory.deleteOne(Review)
to
exports.createReview = () => { handlerFactory.createOne(Review)}
exports.updateReview = () => { handlerFactory.updateOne(Review) }
exports.deleteReview = () => { handlerFactory.deleteOne(Review) }
You should pass a function to the router
Check following syntax:
Router
.route('/:id').post(function(){})
How I want my data to be structured is as follows:
Student -> Reg_num -> someindex (that will start from 1 - like an
unique key) -> course details.
However, the code I wrote gives me an incorrect structure. Can someone help me sort it out.
var db = admin.database();
var ref = db.ref("Students");
var newMessageRef = ref.push();
exports.uploadFile = functions.https.onRequest((req, res) => {
cors(req, res, () => {
var uniqueID = 97888888888888;
if (req.method !== 'POST') {
return res.status(500).json({
message: 'Not allowed'
})
} else {
return newMessageRef.set({
[uniqueID]: {
course: req.body.course,
credits: req.body.credit,
lecturer : 'Prof. Lee'
}
}).then(() => {
res.status(200).json({
message: "okkkkasss"
});
...
Note: The -LC-lS2HPMbZW9AdT19K is a code that was automatically added from the code. This is because I used ref.push()
Do not use ref.push() or ref.set(), but ref.update() as follows:
const db = admin.database();
const ref = db.ref("Students");
//var newMessageRef = ref.push(); <- Don't do that
exports.uploadFile = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const uniqueID = 97888888888888; <- Student ID
const uniqueCourseKey = 0; <- uniqueCourseKey
if (req.method !== 'POST') {
return res.status(500).json({
message: 'Not allowed'
})
} else {
return ref.child(uniqueID).update({
[uniqueCourseKey]: {
course: req.body.course,
credits: req.body.credit,
lecturer : 'Prof. Lee'
}
}).then(() => {
res.status(200).json({
message: "okkkkasss"
});
....
Then you can call again the Cloud Function with e.g. const uniqueCourseKey = 1; and the new node will be correctly added under the StudentID node.
The doc for the update method is here: https://firebase.google.com/docs/reference/js/firebase.database.Reference#update