i'm building a bookstore app with expressjs and using mongodb to store data i have a database of books i want to display to my index.jade but i keep running into an infinite loop
extends layout
block content
.content
each book in books
.book
img(src="/images/dumbk.png", width="150px", height="200px")
h4.book-title #{book.title}
h6.book-dtls Price: #{book.price} SDG
p.book-desc #{book.description}
a.btn.btn-primary.view(href="/books/details/1") View Book
and this is my index.js
var express = require('express');
var router = express.Router();
var Book = require('../models/bookModel');
/* GET home page. */
router.get('/', /*ensureAuthenticated, */function(req, res, next) {
Book.find({}, function (err, books) {
if (err) {console.log(err);}
var model = {books: books}
console.log(model);
res.render('index', /*{books: books},*/ { title: 'Members' });
});
});
after logging model it logged all the books in my database but it does not render them and keeps running an infinite loop when i tried to log a book title by using book.title in the jade file
-console.log(book.title)
result was undefined
so am sure that the error is in the loop and the formatting
when i log the model the output is
[ { _id: the id, title: "book title", author: "author", price: "10", description: "a book description", stock: "5", cover: "dumbk.png" } ]
You cannot use console.log in the jade file because this is supposed to be a client side script. Your "books" object is back at the server, your console.log will be executed later when the client does it. Try this instead (although there are better ways):
extends layout
block content
each book in books
.book
img(src="/images/dumbk.png", width="150px", height="200px")
h4.book-title #{book.title}
h6.book-dtls Price: #{book.price} SDG
p.book-desc #{book.description}
a.btn.btn-primary.view(href="/books/details/1") View Book
script.
const books = JSON.parse("!{JSON.stringify(books)}");
console.log(books[0] && books[0].title);
Update: according to your comments, you didn't even want this, just to get your Jade file to render books. To do that, you should pass the books array to renderer options:
res.render('index', { title: 'Members', books });
Related
I know what the problem is, but can't figure out why it is happening. I have a simple recipe app using express and mongoose. User passes in recipe info via form and is saved to database via mongoose methods. This part seems to work perfectly and when I console.log using test data, I see that the following data is saved:
{
ingredients: [ 'peanut butter', 'jelly', 'bread' ],
_id: 5e47d564f775ce247052d01c,
name: 'pb jelly sammich',
author: 'rob',
oneLiner: 'classic pb jelly sammich',
image: 'picofpbsammich here',
method: 'add all the ingredients together and boom! pb jelly sammich.',
__v: 0
}
(This is also what shows when I check mongo db using db.recipes.find() and also what displays when I pass in the object to my ejs show template.
However, when I access my show route via get request, I get a long error message using the above test data. Here is they key part of the error message:
'Cast to ObjectId failed for value "picofpbsammich here" at path "_id" for model "Recipes"',
I understand what the problem is, but baffled as to why it is happening. Here is my show route:
app.get("/recipes/:id", function (req, res) {
console.log(req.params.id)
Recipe.findById(req.params.id, function (err, foundRecipe) {
if (err) {
console.log(err);
} else {
res.render("show", { recipe: foundRecipe });
}
})
})
console logging the req.params.id as shown above, prints the following:
5e47d564f775ce247052d01c
picofpbsammich here
The first line is the correct ID, the second is obviously not and the cause of the problem, but I have no idea where that could be coming from :S Why would req.params.id be pulling the VALUE of a property that is named something completely different?
I'm new to mongoose so it's probably something silly I'm doing and any explanations appreciated.
Here is the model:
var mongoose = require("mongoose");
let recipeSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
author: String,
oneLiner: String,
ingredients: [String],
image: String,
method: String
})
module.exports = mongoose.model("Recipes", recipeSchema)
You posted the following code:
app.get("/recipes/:id", function (req, res) {
console.log(req.params.id)
Recipe.findById(req.params.id, function (err, foundRecipe) {
if (err) {
console.log(err);
} else {
res.render("show", { recipe: foundRecipe });
}
})
})
And you mention that in the console.log you receive:
5e47d564f775ce247052d01c
picofpbsammich here
Followed by the exception being logged:
'Cast to ObjectId failed for value "picofpbsammich here" at path "_id"
for model "Recipes"',
Makes me logically assume that you are making two requests, one of which the id is not valid, being:
picofpbsammich here
Mongoose is not able to cast this value to an ObjectId, hence you get the exception, which makes sense imo.
I'm stuck on some code in an index.js file that get the home page (called index.hbs), which references to a file called product-seeder.js (containing a loop that goes through all products listed in an array) and every time the loop goes through a listed product, it renders an output on the home page.
The server rendered way more outputs than I had listed products. I know that this is because javascript is asynchronous, but when I used the code from the tutorial(that is supposed to fix the asynchronous problem) and tried to load the server, it just kept loading and won't stop.
I have no clue to why it's doing that. Did I do something wrong? Thanks, help is much appreciated.
My index.js:
var express = require('express');
var router = express.Router();
var Product = require('../models/product');
/* GET home page. */
router.get('/', function(req, res, next) {
Product.find(function(err, docs) {
res.render('shop/index', {title: 'Shopping Cart', products: docs});
});
});
module.exports = router;
My product-seeder.js:
var Product = require('../models/product');
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/shopping', { useNewUrlParser: true });
var products = [
new Product({
imagePath: 'image.png',
title: 'PD 1',
description: 'Product 1',
price: 1
}),
new Product({
imagePath: 'image.png',
title: 'PD 2',
description: 'Product 2',
price: 2
}),
new Product({
imagePath: 'image.png',
title: 'PD 3',
description: 'Product 3',
price: 3
})
];
var done = 0;
for (var i = 0; i < products.length; i++) {
products[i].save(function() {
done++;
if (done === products.length) {
exit();
}
});
}
function exit() {
mongoose.disconnect();
}
My index.hbs:
{{# each products }}
Output text
{{/each}}
The main problem is that in the rendered page there are more products than expected.
On your database, how many products are saved? The same amount of the rendered? If yes, the issue happens when you save your products.
You can rewrite the "save logic" as following
const products = [/* .. things here .. */]
// This save one after another
async function save() {
for (var i = 0; i < products.length; i++) {
await products[i].save(); // this can generate an eslint warning/error
}
}
// This save all products basically together, if products.length is > 10 can be a bad idea
async function save() {
return Promise.all(products.map(prod => prod.save()));
}
save().then(() => mongoose.disconnect());
I'm using the Getting MEAN With Mongo, Express, Angular, and Node by Simon Holmes and in Chapter 7.2.3 Using the API response data section there is some very weird behavior. It seems that html is being generated from res.render() with data not actually passed to it. When the correct data is being passed, different errors occur depending on how the jade file is being manipulated. The following are locations.js, index.js and locations-list.jade files including terminal output showing an expected JSON. All files are located within the app_server folder seen here https://imgur.com/a/Lfaj828
For those not following along in the book this question only refers to module.exports.homelist = function(req, res) {...} being called, this taking place when there is a request from the browser at localhost:3000/. All other request such as localhost:3000/location behave as expected. The key data mentioned in the previous paragraph is the response from the api request called body inside the request object. The res.render() function uses this data for locations and is called responseBody.
There are several options where code is commented out or uncommented in that produce different outputs as seen below. This takes place in the locations.js file in the renderHompage and module.exports.homelist functions except for Option Four. The following are the options
Removing locations: responseBody from var renderHomepage = function(req, res, responseBody) {...} produces the following html seen in this image https://imgur.com/a/KduEdDG
Including locations: responseBody for var renderHomepage = function(req, res, responseBody) {...} produces the following html seen in this image https://imgur.com/a/Ck4FpFl
Using var renderHomepage = function(req, res) {...} produces the same output as the first option.
Removing lines 24 - 26 (everything involving facilities - see the error message from the second option) from locations-list.jade and using Option Two where responseBody is included produces https://imgur.com/a/bYM8mVj and that list of empty locations is more than a hundred so it can be scrolled down for quite a while.
Terminal output when ever localhost:3000 is requested from browser using Option One, Three and Four. Option two is similar except for GET / 500 806.003 ms - 3396 in the proper place.
GET /api/locations?lng=-122.236353&lat=37.485217 200 19.818 ms - 297
[{"distance":285.97478710956176,"name":"Peet's Coffee","rating":0,"facilities":["hot drinks"," food"," classical music"],"_id":"5ae25bd42d69c6abe46ae307"},{"distance":700.0951415645783,"name":"Philz Coffee","rating":4,"facilities":["hot drinks"," food"," power"],"_id":"5ae25c472d69c6abe46ae30a"}]
GET / 304 128.077 ms - -
GET /bootstrap/css/amelia.bootstrap.css 304 3.148 ms - -
GET /stylesheets/style.css 304 4.557 ms - -
GET /javascripts/jquery-1.12.4.min.js 304 8.150 ms - -
GET /bootstrap/js/bootstrap.min.js 304 6.275 ms - -
GET /bootstrap/fonts/glyphicons-halflings-regular.woff 304 0.551 ms - -
locations.js
var request = require('request');
var apiOptions = {
server : "http://localhost:3000"
};
if (process.env.NODE_ENV === 'production'){
apiOptions.server = "https://example.herokuapp.com/"; // not actual location
}
// var renderHomepage = function(req, res) { // uncomment for Option Three
// res.render('locations-list', {
// title: 'Loc8r - find a place to work with wifi',
// pageHeader: {
// title: 'Loc8r',
// strapline: 'Find places to work with wifi near you!'
// },
// sidebar: "Looking for wifi and a seat? Loc8r helps you find places to work when out and about. Perhaps with coffee, cake or a pint? Let Loc8r help you find the place you're looking for.",
// });
// };
var renderHomepage = function(req, res, responseBody) { // comment out for Option Three
res.render('locations-list', {
title: 'Loc8r - find a place to work with wifi',
pageHeader: {
title: 'Loc8r',
strapline: 'Find places to work with wifi near you!'
},
sidebar: "Looking for wifi and a seat? Loc8r helps you find places to work when out and about. Perhaps with coffee, cake or a pint? Let Loc8r help you find the place you're looking for.",
// locations: responseBody // comment out for Option One, uncomment out for Option Two
});
};
/* GET 'home' page */
module.exports.homelist = function(req, res) {
var requestOptions, path;
path = '/api/locations';
requestOptions = {
url: apiOptions.server + path,
method : "GET",
JSON : {},
qs : {
lng : -122.236353,
lat : 37.485217
}
};
request(
requestOptions,
function(err, response, body){
console.log(body);
// renderHomepage(req, res); // use for Option Three
renderHomepage(req, res, body); // use for Option One and Two
}
);
};
/* GET 'Location info' page */
module.exports.locationInfo = function(req, res) {
res.render('location-info', {
title: 'Location info',
name: 'Starcups',
address: '125 High Street, Reading, RG6 1PS',
rating: 5,
facilities: ['Hot drinks', 'Premium wifi'],
distance: '109m',
description: " Simon's cafe is on Loc8r because it has accessible wifi and space to sit down with your laptop and get some work dont.",
times: [
"Monday - Friday : 7:00am - 7:00pm",
"Saturday : 8:00am - 5:00pm",
"Sunday : closed"
],
reviews: [{
name: "Simon Holmes",
description: "What a great place. I can't say enough good things about it.",
timestamp: "16 Febuary 2014",
rating: 3
},{
name: "Judy Holmes",
description: "It was okay. Coffee wasn't great, but the wifi was fast.",
timestamp: "26 January 2015",
rating: 3
},{
name: "Simon Cramp",
description: "It was okay. Coffee wasn't great, but the wifi was fast.",
timestamp: "6 September 2012",
rating: 3
},{
name: "Simoolmes",
description: "What a great place. I can't say enough good things about it.",
timestamp: "14 June 2013",
rating: 3
}]
});
};
/* GET 'Add review' page */
module.exports.addReview = function(req, res) {
res.render('location-review-form', {
title: 'Add review',
name: 'Starcups'
});
};
locations-list.jade
extends layout
include _includes/sharedHTMLfunctions.jade
block content
#banner.page-header
.row
.col-lg-6
h1= pageHeader.title
small #{pageHeader.strapline}
.row
.col-xs-12.col-sm-8
.error= message
.row.list-group
each location in locations
.col-xs-12.list-group-item
h4
a(href="/location/#{location._id}")= location.name
small
+outputRating(location.rating)
span.badge.pull-right.badge-default= location.distance
p.address= location.address
p
each facility in location.facilities
span.label.label-warning= facility
|
.col-xs.12.col-sm-4
p.lead= sidebar
index.js
var express = require('express');
var router = express.Router();
var ctrlLocations = require('../controllers/locations');
var ctrlOthers = require('../controllers/others');
/* Location pages */
router.get('/', ctrlLocations.homelist);
router.get('/location', ctrlLocations.locationInfo);
router.get('/location/review/new', ctrlLocations.addReview);
/* Other pages */
router.get('/about', ctrlOthers.about);
module.exports = router;
So the big question is how the html is displaying locations that aren't being passed into res.render() as can be seen in the first and third options? Option One and Three are obviously very similar but I just had to double check because it just seems so weird. I thought it was possible that the data was being cached somewhere so I changed the latitude longitude numbers in the requestOptions the html shows that change for Option One and Three
Not shown here I've replaced responseBody with hardcoded sample location data from earlier in the book similar to what is seen in module.exports.locationInfo = function(req, res) {...} and everything looked fine.
I have a model Post:
// Post.js
attributes:{
name: {type:"string"},
users: { collection: 'user', via: 'postsReceived' },
category: { model: 'category' },
// Plus lots of other fields and relationships to other models for example category, topic, etc...
}
And I have a model Users:
// Users.js
attributes:{
name: {type:"string"},
postsReceived: { collection: 'post', via: 'users', dominant: true }
}
A post can be sent to many users and a user may have many posts. This if fine however when it comes to populating a list of posts that a user is attached to this becomes quite difficult. I am using the below code to get the list of posts, however this does not populate the posts relationships. For example the below will only return a id for the category of the post. What is the best way (in terms of performance) to get this to work?
User.findOne({id:id})
.populate('postsReceived')
.exec(function(err, user){
if(err) return res.serverError(err);
if (user){
var result = user.postsReceived;
return res.json(result);
}
});
I found a solution for this that seems to work quite nicely from:
This Answer
The answer in the question above suggets using sails-hook-orm-offshore
A webpage sends JSON data via POST to my Node.js App (MEAN-environment using Mongoose). The JSON file looks like this (excerpt):
Firstname: 'XY',
Surname: 'asd',
Articles:
[ { title: '1', description: 'XY' },
{ title: '2', description: 'XY' },
{ title: '3', description: 'XY' }
The purpose is to create an Author in a mongodb database, add the author to an additional directory, store associated articles and create references to those articles in the author document (code excluded). This is the code that handles the request:
[...]
async.waterfall([
//Create random code for author (besides mongodb-specific id)
function(callback){
newAuthorCode.randomAuthorCode(function(err, code) {
callback(null, code);
});
},
//Save new author to db
function(code, callback){
var newAuthor = new Author({firstname: req.body.Firstname,
surname: req.body.Surname,
identcode: code
});
newAuthor.save(function (err){
callback(null, newAuthor);
});
},
//Add new author to an additional directory
function(newAuthor, callback){
Directory.update({_id: req.user._id}, {$push: {authorids: newAuthor._id }}, function(err, update){
if (update){
callback(null);
}
});
},
//saves articles to db
function(callback){
var keys = Object.keys(req.body.articles);
for(var i=0, length=keys.length; i<length; i++){
var newArticle = new Article({title: req.body.Articles[keys[i]].title,
description: req.body.Articles[keys[i]].description
});
newArticle.save(function (err){
console.log(newArticle._id); // <--- !!!!!!!
});
}
callback(null);
}
], function (err, result) {
console.log('DONE!!!');
res.send('200');
});
My problems:
1) The marked line of code where I try to output all IDs of the generated articles only delivers i-times the ID of the last article stored (in this case of article 3).
2) Problem 1 leads to the issue that I can not create references of the newly created articles in the author document (stored in a different collection) as I can't access no article IDs but the last one!?
3) Sometimes the author, as well as the articles, are created multiple times in the database (with huge time gap in between)!?
Thanks for any advice as I am running out of ideas.
Igor
Not sure what newLink is in your code, but you can try this instead:
newArticle.save(function (err, newDoc){
console.log(newDoc._id); // <--- !!!!!!!
});