Skip to content

manaia-2021/yazza-cp02

Repository files navigation

Server-Side Gallery

Build a photo gallery using Handlebars views.

Learning objectives

  1. Understanding server-side rendering with Handlebars.
  2. Practice dividing up a page into views, layouts, and partials.
  3. Practice testing routes.

Getting started

  • After cloning this repo, install dependencies with npm install
  • To start the server : npm start
  • To debug the server (and have it reload with Nodemon after changes): npm run dev
  • To run the tests: npm test

Hello world

When you're learning a new technology, make sure you start simple and get that working before layering on too much complexity.

  1. Start by creating a new home route, / in the server
  • Make sure it's working by having it send something like res.send('Hello, world!')
  1. Express Handlebars requires a default layout in order to render templates.
  • Create a layout file in views/layouts, default is main.hbs (see the docs for more on layouts). It should look just like a standard HTML page, but with {{{body}}} between the <body></body> tags (notice there's three sets of curly braces there, not two)!
  • You can include whatever CSS you like: perhaps Skeleton from a CDN if you just want a quick start?
  1. We've provided a single home.hbs template in the views folder for you. Instead of res.send, now use res.render to render home.hbs template when anyone visits the / route.
  • When you reload the page, you should see the text change to, 'Hello, templates!'
  1. Create an object in your route definition with the property title, like so:
  const viewData = {
    title: 'Gallery'
  }
  1. Pass the object as the second argument to render:
  const template = 'home'
  res.render(template, viewData)
  1. Alter home.hbs to refer to the property using {{title}}. Maybe put it inside <h1></h1> tags?
  • Reload the page: does it work?

You'll find this pattern repeating throughout your exploration of server-side rendering:

  • get some data and put it in an object
  • pass that object to res.render
  • modify the .hbs template to make use of the data

The rest of this exercise should follow the same arc - gradually layering up detail and complexity.

Batteries included

Did you know you could require JSON? Pretty handy when you need a bit of configuration data! You can't leave the extension off like you can with .js, though.

We want you to be exploring and understanding template rendering today, so we've provided you with some data to use that shouldn't take much effort to work with. It's an array of objects brought into the program using require. Each object represents a piece of art.

The objects look like this:

  {
    "id": 1,
    "title": "Kea in Flight",
    "comments": [
      "Very arty."
    ],
    "artwork": "images/kea.jpg",
    "artist": {
      "name": "Ben",
      "url": "https://www.flickr.com/photos/seabirdnz/"
    },
    "license": {
      "name": "CC BY-ND 2.0",
      "url": "https://creativecommons.org/licenses/by-nd/2.0/"
    }
  },

Any time you want to use this data, you can just const art = require('./art.json') at the top of the file you want to use it in. Remember, art is an array and Handlebars expects you to pass it an object, so you might need to do something like this:

  const viewData = {
    title: 'Gallery',
    art: art
  }

MVP

  1. As a user, I want to see a list of artwork titles on the home page so I can see what's available.
  • Remember, you can do something for each element in the art array using {{#each}}.
  • We suggest using an unordered list, where each artwork titles could be listed using <li>{{title}}</li>.
  1. As a user, I want to see who each artwork is by so I can give them credit.
  • Since you already have the title, this should be pretty easy! Do the same thing for the license. (You could even make it a link if you like: the URL property is also included.)
  1. As a user, I want to see what license the artwork is under so I know if I can copy it or not.
  • This {{#each}} block is getting a bit complicated. Let's add a partial! The {{#each}} will stay the same, but you'll move all the code inside it to the partial file (artwork-summary.hbs, for example).
  1. As a user, I want to see a header at the top of the page displaying the page title so I know where I am.
  • We could just "hard-code" this in the template, but to keep our design flexible let's use a partial and we can include a title property on every data object we pass to res.render.
  • Create a header.hbs partial. Make it look however you like, but be sure it has a {{title}} in there somewhere.
  1. As a user, I want to see a footer at the bottom of the page displaying contact details so that I can contact the people responsible for the site.
  • Repetition can be a wonderful thing. Create a footer.hbs partial and include it in your home template.
  • Hint: Move the header and footer partials into the main.hbs layout file, and they'll be used for every template view you create from now on.
  1. As a user, I want to see an artwork displayed when I visit /artworks/:id so that I can view the image.
  • Create artworks.hbs. It doesn't have to be complicated: just a single img tag with its src attribute set to {{artwork}} will do nicely
  • Create a new route in server.js. In the route, you'll need to find the correct artwork using req.params.id. Hint: art.find() (see MDN)
  • Send the artwork to the res.render() call
  1. As a user, I want to be able to click on the artwork title on the home page and be taken to the image view.
  • Time to link it up! In your artwork-summary.hbs (or whatever you called it) partial, turn the artwork title into a link. You'll need to make use of the id property of the artwork object to build your links.

Ready for more?

  1. As a user, I want a link to the home page home from the image view so that I don't need to use the browser back button.
  • Here's another good partial opportunity! What we need is a simple partial that can be inserted anytime we need a link to the home page.
  1. As a user, I want to see all the details on the image view so that I can easily see information about the artist and licence.
  • Although you don't strictly need to create another partial here, it might be a good opportunity to practice. You can even do partials within partials! For example, you could use a comment.hbs partial for each element in the comments array, and use that from an artwork-details.hbs partial.

Write some tests for your routes with Supertest and Cheerio. These testing libraries have already been installed for you - create a server.test.js and test away! Particularly testing both sides of any {{#if}}s you have, and that your {{#each}}s loop correctly, would be a great start!

Take the chance to explore, play, experiment. Ask lots of questions!

Stretch ideas

Including the title in the data object passed to res.render each time works ok, but what if some developer in the future forgets to pass it? It'd be great if there was some way in the template of providing a default title... maybe there's a way using the {{#if}} helper?

You could shift the data access of our art object to a data.js file, and only export utility functions with names like getAll and getById(1).

Even more stretch

Did you know you can define your own Handlebars helpers, like {{#if}} and {{#each}}? Try writing a simple helper that (for example) truncates numbers to display only two decimal places.

Further reading

For more information, check out these links:

Releases

No releases published

Packages

No packages published