3. Displaying All Posts
Alright next step! Now that we can create posts, let's display them.
- Create a post
- Show all posts and show one post
- Make the root route (
/
) go to the/posts/index
route render aposts-index
template - Style the template and loop over the
posts
object - Make route to
/posts/show
route (/posts/:id
) - Style the template and display the
post
object
- Make the root route (
- Show one post
- Comment on posts
- Create subreddits
- Sign up and Login
- Associate posts and comments with their author
- Make comments on comments
- Vote a post up or down
#
Connection ScriptNext, you'll need to handle promises from the Mongoose perspective.
I'm going to encourage the use of promises
to handle asynchronous transactions. As of Mongoose 5.0 native promises are used by default. Read more
Finally, for testing, we can add an error handler for connection errors.
[action] Open
/data/reddit-db.js
and make the following changes to support connection error handling and promises:
mongoose.connect( url, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, useFindAndModify: false, }, (err) => { assert.equal(null, err); console.log("Connected successfully to database");> // db.close(); turn on for testing });mongoose.connection.on('error', console.error.bind(console, 'MongoDB connection Error:'));
Note that the following line we included previously allows us to display debug info from Mongoose in the console:
mongoose.set("debug", true)
#
/Posts/Index Route[action] Next, let's create a
posts-index
template in ourviews
folder. Don't worry about it looking fancy right now, we'll spruce it up in a bit. Just have it displayhello world
for now, or something equally simple.Once that's created, we want to have the root route (
/
) render theposts-index
template. We also need to then pull theposts
out of the database, and send them along with the response. Let's edit ourINDEX
call in ourposts.js
controller. Remember to put it AFTER you require all the middleware:
app.get("/", (req, res) => { Post.find({}) .lean() .then((posts) => res.render("posts-index", { posts })) .catch((err) => { console.log(err.message) })})
Want more of a challenge?
[challenge]
As a stretch challenge try rewritting the code block above to be async/await. Here is are some video resources:
Callbacks vs Promises vs RxJs Observables vs async/await
And also some text resources:
Async/Await Solution
[solution]
We will not give you the solution to every async/await stretch challenge but hopefully this first one gives you a positive direction to head in.
app.get("/", async (req, res) => { try { const posts = await Post.find({}).lean() return res.render("posts-index", { posts }) } catch (err) { console.log(err.message) }})
Now, how clean does that async/await code look?
Try running this and see if your posts-index
is being displayed. If so, replace your hello world
with the variable {{posts}}
. What do you see?
Now, back to those posts
! Let's fix them up and display the them properly.
#
Styling and Looping Over Posts[action] If you haven't already, let's go back to our layout template
main.handlebars
and put the whole{{{body}}}
object into a div with a container class.
<div class="container">{{{body}}}</div>
Great, now let's go back to posts-index.handlebars
and make it look good!
[action] To start, we'll put the list of posts into the middle 8 columns of the grid.
<div class="row"> <div class="col-sm-8 col-sm-offset-2">{{!-- more code will go here --}}</div></div>
Now that we have {{posts}}
, we can use handlebars' built in each
operator to loop over the posts, and display each one.
[action] In each post, use bootstrap's
list-group
andlist-group-item
classes. Display the post title in a div with the classlead
, and add an anchor tag that links to the post's url. Finally, addtarget="_blank"
to the anchor tag, so that the url opens in a new tab. Here's the fullposts-index.handlebars
:
<div class="row"> <div class="col-sm-8 col-sm-offset-2"> <ul> {{#each posts}} <li class="list-group-item"> <div class="lead">{{this.title}}</div> <a href="{{this.url}}" target="_blank">{{this.url}}</a> </li> {{/each}} </ul> </div></div>
#
Now Commit$ git add .$ git commit -m 'Users can view a list of posts'$ git push
#
Viewing One PostIn order to view a single post when a user clicks on it, we'll need to establish a route for individual posts, and render them in their own template.
Let's begin with the user action - clicking on a post in the post-index
template.
[action] Replace the current
div
for displaying a post'stitle
with the following:
<div class="lead"><a href="/posts/{{this._id}}">{{this.title}}</a></div>
The title is a link to the show page. If we click it, what happens? Error! No route! It's time to fix that.
#
/Posts/Show RouteWe need the path /posts/:id
to resolve to displaying a posts-show
template.
[action] open
controllers/posts.js
, and add a new GET endpoint. Make sure all middleware requirements happen ABOVE it:
// LOOK UP THE POSTapp.get("/posts/:id", (req, res) => { Post.findById(req.params.id) .lean() .then((post) => res.render("posts-show", { post })) .catch((err) => { console.log(err.message) })})
Async/Await stretch challenge!
[challenge]
Refactor the code block above to be async/await. If you get stuck, there are video and text resources linked at the first async/await stretch challenge.
What happens if we refresh? No template!
#
Making the TemplateTime to template. As a bare minimum we'll use some bootstrap classes to make things look reasonable as we continue to develop the application.
[action] In your
views
folder, makeposts-show.handlebars
with the following template:
<div class="row"> <div class="col-sm-6 col-sm-offset-3"> <a href="{{post.url}}" class="lead">{{post.title}}</a> <p>{{post.summary}}</p> </div></div>
#
Product So FarNow can you see your post?
What about clicking into your post?
#
Now Commit$ git add .$ git commit -m 'Users can see individual posts'$ git push