Hapi handlebar template not displaying json object data - node.js

Hey guys I have this simple handler that reads a mysql table and returns the json obj to the route like so.
handler
var PostStore = {};
PostStore.getAllPosts = function(){
conn.query('SELECT * FROM posts',function(err, result){
if(err){
console.log(err);
}
console.log(JSON.parse(JSON.stringify(result)));
return JSON.parse(JSON.stringify(result));
});
}
module.exports = PostStore;
router
{
path: '/',
method: 'GET',
handler: function (request, reply) {
console.log(PostStore.getAllPosts);
reply.view('index', {
title: 'My home page',
posts: PostStore.getAllPosts
});
}
}
index.html
<h1>{{title}}</h1>
{{#each posts}}
<h1>{{this.title}}</h1>
{{/each}}
Here is what the console output looks like
[Function]
[ { id: 1,
title: 'Hello World',
body: 'My First Post on this cool Hapi Blog!',
date: null } ]
As you can see the sql result is parsed in to a JSON obj but not read from handlebars. Also note that the {{title}} is displaying "My home page" as expected.
Any help would be much appreciated! Thank you.

PostStore.getAllPosts is async, you need to render the view inside it's callback function.
PostStore.getAllPosts(function (err, posts) {
// render the view
});

The only way to render async method in view is to call this methods in a pre handler and assign the return value so you can render the returned data inside the view. See here in hapi's documents for further explanation http://hapijs.com/api#route-prerequisites.
In PostStore routines this code
PostStore.getAllPosts = function(callback){
conn.query('SELECT * FROM posts',function(err, result){
if(err){
return callback(err);
}
return callback(result);
});
}
Then in handlers code
const getPosts = function (request, reply) {
... // do something async to set posts
PostStore.getAllPosts(function(err, results) {
if (err) {
return reply('Error occurred')
}
reply(results);
};
};
server.route({
method: 'GET',
path: '/',
config: {
pre: [
{ method: getPosts, assign: 'posts' }
],
handler: function (request, reply) {
reply.view('index', {
title: 'My home page',
posts: request.pre.posts,
});
}
}
});

Related

MongoDB values being stored as null when using fetch in React

I've followed MongoDB's 'How To Use Mern Stack' tutorial to the letter, but I'm still getting null values stored in MongoDB. The console.log in the code does not appear, so I'm struggling to debug what's going on. Here's my code:
Client side
async function onSubmit(e) {
e.preventDefault();
// When a post request is sent to the create url, we'll add a new record to the database.
const newPerson = { ...form };
await fetch("http://localhost:5000/record/add", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(newPerson),
}).catch((error) => {
window.alert(error);
return;
});
setForm({ name: "", position: "", level: "" });
navigate("/");
}
Server side
recordRoutes.route("/record/add").post(function (req, response) {
console.log("hi");
let db_connect = dbo.getDb();
let myobj = {
name: req.body.name,
position: req.body.position,
level: req.body.level,
};
db_connect.collection("records").insertOne(myobj, function (err, res) {
if (err) throw err;
response.json(res);
});
});
Thanks for your help!

UnhandledPromiseRejectionWarning in Node App

I'm building a crypto tracker in node. I have a list of addresses in the Wallet collection. I'm wanting to perform an API call to ethplorer for each address. I'm getting the error UnhandledPromiseRejectionWarning and also req.next is not a function. I'm confused because I'm not even using a req.next anywhere.
Any idea what's causing this error?
app.get('/ethplorer', function(req, res) {
const rp = require('request-promise');
Wallet.find({}).then(function(wallets) {
var allData = [];
wallets.forEach(function(w) {
const requestOptions = {
method: 'GET',
url: `https://api.ethplorer.io/getAddressInfo/${w.address}`,
qs: {
'apiKey': 'aaa'
},
json: true
};
rp(requestOptions).then(response => {
allData.push(response);
}).catch(function(err) {
console.log(err);
});
res.render('ethereum', {user: req.user, eth: allData});
});
});
});
allData is not going to be populated, nor should you do res.render in a loop
Rewritten to use async/await, avoid then() callbacks its messy
const rp = require('request-promise')
app.get('/ethplorer', async function (req, res, next) {
try {
const requestOptions = {
method: 'GET',
qs: {
'apiKey': 'aaa'
},
json: true
}
let allData = []
for (let wallet of await Wallet.find({})) {
try {
allData.push(await rp({
...requestOptions,
url: 'https://api.ethplorer.io/getAddressInfo/' + wallet.address
}))
} catch (_) {}
}
res.render('ethereum', {
user: req.user,
eth: allData
})
} catch (e) {
next(e)
}
})

Mongoose Update Doesn't Run Before My Res.Send()

I currently have a controller which is handling the onboarding of a user. When the user completes their onboarding flow, I update their status in Mongo from New to Active, then send them to a new page. As a method of security, I also have a middleware function on every authenticated route which checks if the user is logged in, as well as their status. If their status is New, I send them to the onboarding flow (because theoretically they haven't seen the onboarding flow).
As I run through my experience, when I submit the onboarding flow, I get redirected back to the beginning of the flow. I check Mongo and my status is no longer New, so I was confused why this was happening. Eventually I realized when I am sending the user to a new page, the authentication route is checking the user's status before my findOneAndUpdate() has had a chance to complete. So the user gets redirected back to the onboarding flow because the last query didn't finish in time.
Any idea how to fix this? I assume it has something to do with async/await but I'm not sure. Here's my code below, I'm working in Node.JS with an express framework. Also, in my post onboarding I am using a mapbox api to get the lat/long of their zip code, which is why I have the request.get() in the code.
Onboarding Controller
exports.postOnboarding = (req, res, next) => {
var data = req.params.data;
var final = data.split(',');
location = final[4].toString();
url = "https://api.mapbox.com/geocoding/v5/mapbox.places/" + location + ".json";
request.get(url)
.query({access_token: "private_key"})
.end(function(err, result) {
User.findOneAndUpdate(
{"credentials.userId": req.session.user.credentials.userId },
{ practiceSettings: {
businessType: final[2],
experienceType: final[0],
fullFee: final[3]
},
credentials: {
userType: "Active",
active: true,
userId: req.session.user.credentials.userId,
provider: "local"
},
paySettings: {
q1: "undeternmined"
},
license: final[1],
zip: final[4],
latLong: result.body.features[0].center
}, (err, result) => {
if (err) {
console.log(err);
} else {
console.log("settings updated");
res.redirect('/dashboard');
}
}
)
}) };
Dashboard Route
router.get('/dashboard', isAuth, adminController.getDashboard);
isAuth Middleware
const User = require('../models/user');
module.exports = (req, res, next) => {
if (!req.session.isLoggedIn) {
return res.redirect('/login');
} else if (req.session.user.credentials.userType == 'Unverified') {
return res.redirect('/login?verified=false');
} else if (req.url == '/onboarding') {
return next();
}
User.findOne({"credentials.userId" : req.session.user.credentials.userId})
.then(result => {
res.locals.user = result;
if (req.session.sidebarStatus == 'closed') {
res.locals.sidebarStatus = 'closed';
}
if (result.credentials.userType == 'New') {
return res.redirect('/onboarding');
}
next();
})
}
And for reference, below is a snippet of my onboarding.ejs file which calls the post route. This isn't the whole thing, I have a lot of nested Sweet Alert modals, but this is the important part.
Swal.fire({
text: "Question",
width: "90%",
input: "text",
inputPlaceholder: "92805",
inputValidator: (value) => {
if (!value) {
return 'You must fill in this field.'
}
if (value.length != 5) {
return 'Please use a 5 digit zip-code as your answer.'
}
},
showCancelButton: false,
confirmButtonText: 'Submit',
backdrop: '#FFFFFF',
allowOutsideClick: false
})
.then((result5) => {
res3 = result3.value.replace(",", "");
final = [result1.value, result2.value, res3, result4.value, result5.value];
$.ajax({
url: "/post-onboarding/" + final,
dataType: 'json',
type: 'post',
success: function (data) {
if ( data.length ) {
Swal.fire({
title: 'Error!',
text: 'Something bad happened',
icon: 'error',
confirmButtonText: 'OK'
});
} else {
//redirect user
}
}
});

Render EJS - Node JS

I would like to update my view after an ajax call, rendering compiled ejs from the server.
These two previous questions seem to achieve this but I cannot update my view
Can't Render EJS Template on Client
How to generate content on ejs with jquery after ajax call to express server
So from what I understand I should compile my ejs file (partial) on the server.
fixtures.ejs
<% fixtures.forEach((fixture) => { %>
<h2><%= fixture.home_team %> vs <%= fixture.away_team %></h2>
<% }) %>
index.js
app.post('/league_fixtures', async function (req, res) {
try {
var league_name = req.body.league_name;
const fixtures = await leagueFixtures(league_name);
//compile view
fs.readFile('./views/fixtures.ejs', "utf-8", function(err, template) {
fixture_template = ejs.compile(template, { client: true });
var html = fixture_template({fixtures: fixtures});
console.log(html);
// This logs out my HTML with fixtures so I am almost there
// <h2>Club Africain vs Al-Faisaly Amman</h2>
// <h2>Al Nejmeh vs ASAC Concorde</h2>
});
res.json({fixtures: fixtures });
} catch (err) {
res.status(500).send({ error: 'Something failed!' })
}
});
Ajax
$("a.league-name").on("click", function (e) {
e.preventDefault();
var league_name = $(this).text().trim();
$.ajax({
url: '/league_fixtures',
type: 'POST',
dataType: "json",
data: { league_name: league_name },
success: function(fixtures){
// How do i get HTML from server into here ?
$('#panel_' + league_name).html(fixtures);
},
error: function(jqXHR, textStatus, err){
alert('text status '+textStatus+', err '+err)
}
})
});
});
I don't get any errors when I fire the ajax request but I also do not get any data or HTML updated in my div.
What am I doing wrong?
So I finally got a working solution:
index.js
app.post('/league_fixtures', async function (req, res) {
try {
const league_name = req.body.league_name;
const fixtures = await leagueFixtures(league_name);
const file = await readFile('./views/fixtures.ejs');
var fixture_template = ejs.compile(file, { client: true });
const html = fixture_template({fixtures: fixtures});
res.send({ html: html });
} catch (err) {
res.status(500).send({ error: 'Something failed!' })
}
});
ajax call
$.ajax({
url: '/league_fixtures',
type: 'POST',
dataType: "json",
cache: true,
data: { league_name: league_name },
success: function(fixtures){
var html = fixtures['html'];
$('#panel_' + league_name).html(html);
},
error: function(jqXHR, textStatus, err){
alert('text status '+textStatus+', err '+err)
}
})

Unable to get params Node and Angular

I have a list of todos that are fetched from MongoDB and they are displayed on one page and when I click on one it opens on another page with URL that equals clicked todo. Now I am trying to get the id from URL and send it to node server but I can't get it.
ngOnInit() {
this.route.params
.subscribe(
(params: Params) => {
let todoId = params['userId'];
console.log(todoId);
}
);
}
console returns undefined.
I found one solution where id is fetched by this line of code, but it only gets id once and when I click on another todo it doesn't log anything.
let id = this.route.snapshot.paramMap.get('id');
console.log(id)
And when I want to send request to server with this:
let id = this.route.snapshot.paramMap.get('id');
this.todoService.getSingleTodo(id)
.subscribe(
(todo: Todo) => {
this.todo = todo;
console.log(todo);
}
);
I got this error in console "message":"Cast to ObjectId failed for value \":id\" at path \"_id\"
Service looks like this:
getSingleTodo(id) {
return this.http.get('http://localhost:3000/todos/:id')
.map( response => response.json().obj)
.map( todo => todo.map(todo => new Todo(todo.todoHeadline,
todo.todoDescription, todo._id)));
}
And node file:
router.get('/:id', (req, res, next) => {
console.log(req.params.id);
Todo.findById(req.params.id, (err, singleTodo) => {
if (err) {
return res.status(500).json({
title: 'An error occured',
error:err
});
}
res.status(201).json({
message: 'Success',
obj: singleTodo
});
});
});
Also this console prints :id.
Main routing file
const APP_ROUTES: Routes = [
{ path: '', redirectTo: '/auth', pathMatch: 'full' },
{ path: 'todos', component: TodoComponent, children: TODO_ROUTES},
{ path: 'auth', component: AuthenticationComponent, children: AUTH_ROUTES }
];
And children routes for todos
export const TODO_ROUTES: Routes = [
{path: 'todo/add', component: TodoAddComponent},
{path: ':id', component: TodoListComponent},
{path: 'edit', component: TodoEditComponent}
];
HTML where all todos are displayed lools like this
<ul class="list-group">
<li class="list-group-item"
*ngFor="let todo of todos;"
[routerLink]="['/todos', todo.todoId]">{{todo.todoHeadline}}
</li>
</ul>
What could be the problem?
It's this line right here that's wrong:
let todoId = params['userId'];
In your Routing, you defined that the path variable would be named "id":
{path: ':id', component: TodoListComponent},
So when you try to access a path variable userId, it obviously returns undefined.

Resources