Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialJonathan Grieve
Treehouse Moderator 91,253 PointsFlashcard template not working
Have there been any breaking changes to Express.js since this course was released? I don't understand why, but my query strings are not working. I can open up the server but as soon as I try to type the URL with the query string it crashes the app.
http://localhost:3000/cards/1?side=question
_http_outgoing.js:491
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:491:11)
at ServerResponse.setHeader (_http_outgoing.js:498:3)
at Array.write (C:\xampp\htdocs\jgdm-100daysofcode\nodejs\express_site\node_modules\finalhandler\index.js:285:9)
at listener (C:\xampp\htdocs\jgdm-100daysofcode\nodejs\express_site\node_modules\on-finished\index.js:169:15)
at onFinish (C:\xampp\htdocs\jgdm-100daysofcode\nodejs\express_site\node_modules\on-finished\index.js:100:5)
at callback (C:\xampp\htdocs\jgdm-100daysofcode\nodejs\express_site\node_modules\ee-first\index.js:55:10)
at IncomingMessage.onevent (C:\xampp\htdocs\jgdm-100daysofcode\nodejs\express_site\node_modules\ee-first\index.js:93:5)
at emitNone (events.js:106:13)
at IncomingMessage.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1064:12)
Code as below.
extends layout.pug
block content
h1 Flash Cards
section#content
h2= text
if hint
p
i Hint: #{hint}
else
p
i Hint: (No Hint available)
const express = require('express');
//import body-parser middleware
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser");
//Express launch function
const app = express();
//set the view engine to use pug for templating
app.set("view engine", "pug");
//use bodyParser and cookieParser middleware - third party.
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookieParser());
const indexRouter = require("./routes");
const cardsRouter = require("./routes/cards");
app.use(indexRouter);
app.use('/cards', cardsRouter);
/*MIDDLEWARE*/
/* app.use((req, res, next) => {
console.log("Hello");
next();
});
app.use((req, res, next) => {
console.log(", World");
next();
}); */
//404 Middleware!
app.use((res, req, next) => {
const err = new Error('Page Not Found!');
err.status = 404;
next(err);
});
//error handler middleware
app.use((err, req, res, next) => {
// do something
//res.status(err.status)
res.locals.error = err;
res.status(err.status);
res.render('error');
next();
//res.end();
});
//Set up the development server listen method - port number
app.listen(3000, function(){
console.log("server currently running on Heroku");
});
const express = require("express");
const routes = express.Router();
//list data
const names = [
"Jack Bauer",
"Jill Green",
"John Joe",
"Jonah Whale",
"Jamaal LH",
"Joe Fisher"
]
/*ROUTES*/
//serve the home route
routes.get('/', (req, res) => {
const name = req.cookies.username;
//basic response with the send method
if(name) {
res.render('index', {name, page_title: "Flash Card App"});
} else {
res.redirect('/hello');
}
//res.end();
});
//serve the register route
routes.get('/register', (req, res) => {
//basic response with the send method
res.render('register', {page_title: "Flash Card App: Register of users", names});
//res.end();
});
//serve the 4th route which will be a post route
routes.get('/hello', (req, res) => {
//store cookie
const name = req.cookies.username;
//perform actions based on setting of cookie
if(name) {
res.redirect('/');
} else {
res.render('hello', { page_title: "Flash Card App: Hello Route"});
}
//res.end();
});
routes.post('/hello', (req, res) => {
const name = req.body.username;
//basic response with the send method,
res.cookie('username', name);
res.redirect('/');
//console.log(req.body);
//res.end();
});
routes.post('/goodbye', (req, res) => {
//res.clearCookie(req.cookies.username);
res.clearCookie('username');
res.redirect('/hello');
});
//export index routes to app.js
module.exports = routes;
const express = require("express");
const routes = express.Router();
const { data } = require("../data/flashcardData.json");
const { cards } = data;
//serve the cards route
routes.get('/:id', (req, res) => {
const { side } = req.query;
const { id } = req.params;
const text = cards[id][side];
const { hint } = cards[id];
const templateData = { text, hint };
res.render('card', templateData);
});
//export cards routes to app.js
module.exports = routes;
Jonathan Grieve
Treehouse Moderator 91,253 PointsYea I probably should be logging things a bit more. I’ll look at that.
Changing to req.query.side
doesn’t seem to have an effect either.
I don’t think we’re supposed to be making any modifications to the JSON. Or anything that’s coded in the video which is what makes me think the method has changed a bit over the years.
Blake Larson
13,014 PointsAre you are getting the right return from req.query.side? Have you used a zero or one instead of question? I'm guessing it's formatted like [['question text', 'answer text], ['question text', 'answer text'], ['question text', 'answer text']] in the data file, no?
Jonathan Grieve
Treehouse Moderator 91,253 PointsRight... I'm getting "question" logged out from req.query.side
which I think is what we're after.
The log for cards[id][side]
is logging undefined so that seems to be where the problem lies. I changed the line back to const { side } = req.query;
removes that undefined log.
Truth is I'm utterly lost.
Jonathan Grieve
Treehouse Moderator 91,253 PointsI have at least now verified my confused rambling about the code being out of date is nonsense by downloading Andrew's code. Now to look in and seek out the bug!
Sean T. Unwin
28,690 PointsYou're getting the data
object from the .json
file, I assume. Is cards
being set? Is there a match in there for cards[1].question
(assuming the json
is an array), for example the key is definately 'question`?
Jonathan Grieve
Treehouse Moderator 91,253 PointsYes, I don't think there's any question of the data being correctly hooked up. My console logs come up trumps for the side and question data. Everything just seems to fall apart when I try to input route parameters and query strings in the URL .
Truly I think all that part is actually done correctly. Where I think it's falling apart is there is one get route "register" that doesn't actually appear in the course. I have an array called "data" in the index route as well as it's own file called data.js but that file is redundant and isn't actually being used.
The idea of me doing this is so I can expand it when the course is done and to prove to myself i can practice all of this. But it's like one problem substituting for another at the moment. :)
Blake Larson
13,014 PointsI'm pretty sure the the cards[id][question] return is undefined because that statically comes out to cards[1]['question']. That's not going to return a valid value which is trying to be used in the pug file as text which is gonna cause a runtime error. If you write that line as
const text = cards[id][question] ? 'This is a placeholder for a .json file' : 'No text returned';
The text in the card.pug should be no text returned.
Jonathan Grieve
Treehouse Moderator 91,253 PointsThat's not what I get though when I log that output. So confusing!
I'd link you guys to a Repo for this so you could have a proper look but it's linked to a Repo I'm doing for #100DaysOfCode so I'm reluctant to do that.
This is frustrating though because if it wasn't for this, I'd have a functional App! 🤣
Blake Larson
13,014 PointsStatically set the text then.
const text = 'Static Text';
If that doesn't render then it has to be something with the data because everything else in the route makes sense. I still don't know why cards[id] is an array of arrays and an array of objects.
Sean T. Unwin
28,690 PointsShould const templateData = { text, hint };
be const templateData = { text: text, hint: hint };
, meaning to assign the key values to the appropriate variable?
Jonathan Grieve
Treehouse Moderator 91,253 PointsHi guys.
I want to thank you both for sticking around the thread with me.
Let's see if we can nip this in the bud. I've created a separate repository with my latest changes. I've finished following the course up to the last section, so it doesn't serve static files yet beyond Pug and JS.
Hopefully the answer is within. :)
https://github.com/jg-digital-media/express_site
Don't forget to npm install
obviously.
Blake Larson
13,014 PointsYeah try
const { question } = cards[id];
const template = { text: question, hint };
You were referencing a 2d array before. I'm guessing the hint value is correct when you logged it? If not you could try to use JSON.parse(cards) because with the cards are not going through the request so the body parser middleware wouldn't do that automatically. Hopefully that works, I'm on mobile and will try to use the code when I i get home if it's still a problem. Seeing the json file makes me think this will work though, and this is for a hard coded query string with a question. When requesting the answer it would have to be different.
2 Answers
Blake Larson
13,014 PointsSo i ran it and this worked for just getting the id, text, and hint rendered dynamically with no errors.
routes.get("/:id", (req, res) => {
const { side } = req.query;
const { id } = req.params;
let text = "";
let hint;
if (side === "question") {
text = cards[id].question;
hint = cards[id].hint;
} else {
text = cards[id].answer;
}
const templateData = {
id,
hint,
text
};
res.render("cards", templateData);
});
but the middleware has to change to
//error handler middleware
app.use((err, req, res, next) => {
res.locals.error = err;
const status = err.status || 500;
res.status(status);
res.render("error");
});
I kinda gutted that route but now you can add the the display values in the dataTemplate. I typed in answer as the query and that also worked. It was a weird middleware glitch that couldn't inject the id. Weird..
Sean T. Unwin
28,690 PointsNice catch. I'm at work so haven't been able to look at the repo.
Jonathan Grieve
Treehouse Moderator 91,253 PointsI have a very horrible feeling I've led you both down a wild goose chase for nothing.
If you look at the layout.pug file you'll find an attempt to link a stylesheet directly into the pug template. There is a stylesheet there in the project but it shouldn't be there as that's not the way you serve those assets. Anyway I removed that line so the header is not trying to send that anymore.
The app is now working flawlessly. I've just spent the last 2 hours trying to work out why the question/answer and hint text wasn't showing up after Blake's edit. Now I know why.
I'm so sorry!
<!DOCTYPE html>
html(lang="en")
head
meta(charset="UTF-8")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
meta(http-equiv="X-UA-Compatible", content="ie=edge")
title= page_title
// Stylesheets
//link(rel="stylesheet" type="text/css" href="./style.css")
body
include includes/header.pug
if name
h2 Welcome, #{name}
form(action="/goodbye" method="post")
button.submit Goodbye!
p
a(href='/cards') Begin
block content
include includes/footer.pug
Blake Larson
13,014 PointsThat's funny I was wondering why I was getting 0 and style.css when I tried to log the id. Was confusing. Either way it worked!
Jonathan Grieve
Treehouse Moderator 91,253 PointsBelieve me so was I. It was staring me in the face and screaming from the console! Live and learn, as they say!
Blake Larson
13,014 PointsBlake Larson
13,014 PointsWhat is cards[id][side] returning if you log it (if it gets that far)? Probably best off logging cards and req.query.side before declaring anything to make sure everything is right and the problem is in the route block.
I can't see the json data, but is there supposed to be a conditional for if side is 'question' text = cards[1][0] else cards[1][1] or am I see this wrong?