Run the following command from root folder of the cloned project to install all dependencies.
npm install
In order to verify that everything is setup correctly, run the following command, which should show you the failing tests. This is good! We'll be fixing these tests once we jump into the build step.
npm run test
Every time you want to check your work locally you can type that command, and it will report the status of every task in that module.
As you move through the modules, you can run module-specific tests with the script npm run test:module1
, replacing the number with one that corresponds with the module you are working in.
In order to see your changes in a browser, you can run npm start
to start the application, and when you visit http://localhost:3000
in a browser, you should see your components rendered.
@app-require-built-ins In app.js
require the built-in library fs
and store a reference to it in a const
called fs
. Next, require the built-in library path
and store a reference to it in a const
called path
.
@app-require-express-const-app In app.js
, require the express framework and store a reference to it in a const
called express
. Next, call the express function and store it in a const
called app
.
@app-set-views-directory-engine Still in app.js, use the set
function of your newly created app
to configure the directory where our views
can be found. Using the same set
function, set the view engine
to ejs
. Hint: path.join()
& __dirname
Carlos comment #1: Just by reading the directions it wasn't clear to me at first the views
directory is called views
. Perhaps we can be a little more explicit and mention that ? I think this is also the default directory ejs looks for, so it might not be needed unless you wanted to teach them to be explicit.
Carlos comment #2: My personal approach to starting a new Express app from scratch is to start with "the simplest thing that could possible work". This usually means not doing any templating or asset config until running the server for the first time. After the two require
statements, I usually jump straight into writing a simple get("/")
end point which returns hardcoded data (like a simple "Hello from express") using something like response.json()
or even just a 200 status code with response.sendStatus()
. This is to make sure the few lines of code I wrote (the require
statements and whatnot) are correct before moving on.
@app-use-express-static All of our CSS/JS for the client-side is found in the public
directory. We need to point express to public
.
- In app.js call the
use
function ofapp
with a call to theexpress.static()
function as the only parameter. express.static()
should be passed the full path to thepublic
directory. Hint:path.join()
&__dirname
Carlos comment: Similar to how the views
directory works, I believe the "public"
directory is the default when using express.static()
so passing "public" as argument might not be necessary. Not sure if you were going for explicit, but just wanted to mention.
@index-ejs-create-view-file Create a new file called index.ejs
in the src/views/
directory.
@index-ejs-create-view In the newly created file index.ejs
complete the following:
- Include
header.ejs
Hint: <%- %> - Add a
div
element with a class ofcontainer
. - In the container div display the value of the
title
key in anh1
element. Hint: <%= %> - Add an anchor element below the
h1
that points to the/profile
URL path, and has the text contentProfile
. - Below the container div add a line break and another anchor element that points to the
/transfer
URL path with the text contentTransfer
. - Include
footer.ejs
Hint: <%- %>
@app-get-index-route In app.js
create a get
route that points at the root URL path '/'. Render the index
view (created in the next step) and pass an object with a single key value pair, title: 'Index'
.
@app-listen-console-log In app.js
using the listen
function to create a server that listens on port 3000
and then prints the message PS Project Running on port 3000!
to the console after the server is created.
@app-read-account-data In app.js
above the index route, use the readFileSync
function of the built-in fs
library to read the contents of the file located at src/json/accounts.json
. Declare a const
called accountData
to store the contents of the file. accountData
now contains JSON, use JSON.parse
to convert it to a javascript object. Declare a const
called accounts
to store this javascript object. Note: read the file with the UTF8
encoding.
@app-read-user-data In app.js
near the index route, use the readFileSync
function of the built-in fs
library to read the contents of the file located at src/json/users.json
. Declare a const
called userData
to store the contents of the file. userData
now contains JSON, use JSON.parse
to convert it to a javascript object. Declare a const
called users
to store this javascript object. Note: read the file with the UTF8
encoding.
@app-update-index-route In app.js
update the object passed to the existing index route. The title
should be Account Summary
. A new key value pair should be added, accounts: accounts
.
@index-ejs-update-view In index.ejs
and after the container div, add the ejs markup to include the summary
view for each account in the accounts
variable, savings, checking, and credit. Hint: you will have three include statements(<%- %>
), each include
function will be passed a different account, i.e { account: accounts.checking }
.
@app-get-savings-account-route In app.js
near the index route, create a get
route that points at the /savings
URL path. Render the account
view and pass an object with the following key value pair:
account: accounts.savings
@app-get-other-account-routes Now that you have created the savings account route, create similar routes for the checking and credit accounts in the app.js
file.
@account-ejs-show-transactions In account.ejs
after the header markup, add the ejs markup to include the transactions
view. Pass the include function an object with the following key value pair:
account: account
@profile-ejs-create-view-file Create a new file called profile.ejs
in the src/views/
directory.
@profile-ejs-create-view In the newly created file profile.ejs
complete the following:
- Include
header.ejs
Hint: <%- %> - Add an
h1
element with the text contentProfile
- Add a
div
element below theh1
that displays each detail of theuser
object (passed to the view in the next step) on a new line. The details are user.name, user.username, user.phone, user.email, and user.address. - Below the
div
add a line break, then an anchor element that points to the root URL path and has the text contentBack to Account Summary
. - Include
footer.ejs
Hint: <%- %>
@app-get-profile-route Back In app.js
below the account get routes create a get
route that points at the /profile
URL path. Render the profile
view and pass an object with the following key value pair:
user: users[0]
@app-urlencoded-form-data In app.js
near your other app.use statement add express middleware to handle POST data. With the use
function add the express.urlencoded
middleware to app
. Make sure to set the extended
option to true
.
@app-get-transfer-route Near your other routes in app.js
create a get
route that points to the /transfer
URL path. It should render the transfer
view.
@transfer-ejs-update-view In transfer.ejs
in the src/views/
directory complete the following:
- Add the necessary attributes to the
transferForm
so that it posts to a transfer route. - Add a
name
andid
attribute with a value offrom
to the first select. - Add a
name
andid
attribute with a value ofto
to the second select.
Carlos comment: Should we make the POST requirement stand out a little bit more on the first step ? Something like "Add the necessary attributes to the transferForm
so that it issues a POST
form request to the /transfer
route."
@app-post-transfer-route Switch back to app.js
and create a post
route that points to the /transfer
URL path. We will fill in the body of the function for this route in the next few steps.
Carlos comment: I really like the flow on this one! Going from get route -> get form -> post route feels very similar to coding in real life.
@app-post-transfer-route-from-balance Still in app.js
and in the function body of the post route we are going to calculate the new balances for the account we are transferring from.
We have several values that have been entered into the HTML form in transfer.ejs
. Upon form submission the request body will contain from
, to
, and amount
. Current account balances are stored in the accounts
object. As an example to access the current balance for the savings account use accounts["savings"].balance
. Using these values, calculate the new balance of the account we are transferring from. Then set the balance to that amount.
Carlos comment: This is the first step which made me feel a little lost. By reading the instructions I can understand all the values I'm getting from the form submission but I'm confused as to what exactly I'm supposed to code. Maybe this can be broken down into smaller steps, or perhaps be a little more descriptive towards what we want students to do - kind of like how 3.10 is written.
@app-post-transfer-route-to-balance Still in app.js
and in the function body of the post route we are going to calculate the new balances for the account we are transferring to.
We have several values that have been entered into the HTML form in transfer.ejs
. Upon form submission the request body will contain from
, to
, and amount
. Current account balances are stored in the accounts
object. As an example to access the current balance for the savings account use accounts["savings"].balance
. Using these values, calculate the new balance of the account we are transferring to. Then set the balance to that amount. Hint: you will need to use parseInt()
@app-post-transfer-route-write-json Still in app.js
and in the function body of the post route, convert the accounts
javascript object to a string using the JSON.stringify
function save this string in a variable called accountsJSON
.
Still in app.js
and in the function body of the post route, use the writeFileSync
function of the built-in fs
library to write the variable accountsJSON
to the file located at json/accounts.json
.
Notes:
- If at any point
accounts.json
gets overwritten and you would like the original back copy the JSON fromaccount_backup.json
toaccount.json
- Specify the absolute file path using
path.join
&__dirname
- Write the file with the
UTF8
encoding.
@app-post-transfer-route-redirect Still in app.js
and in the function body of the post route, render the transfer
view and pass an object with the following key value pair:
message: "Transfer Completed"
@app-payment-feature The payment feature of the application is similar to the transfer feature. Add this new feature using this general outline of the steps:
- Near your existing routes in
app.js
create a get route with a URL path of/payment
that renders thepayment
view. - Below the payment get route create a post route with a URL path of
/payment
and an empty function. - In the body of the payment post route function:
- Subtract
req.body.amount
fromaccounts.credit.balance
and save it back toaccounts.credit.balance
- Add
req.body.amount
toaccounts.credit.available
and save it back toaccounts.credit.available
remember to useparseInt()
on both values when adding. - Convert the
accounts
javascript object to a JSON string and save it to a variable calledaccountsJSON
- Write
accountsJSON
to a file. Note: write the file with theUTF8
encoding. - Render the
payment
view and pass an object with the following key value pair,{ message: "Payment Successful", account: accounts.credit }
- Subtract
@data-js-create-file Create a new file called data.js
in the root of the src/
directory.
@data-js-require-built-ins In data.js
require the built-in library fs
and store a reference to it in a const
called fs
. Next, require the built-in library path
and store a reference to it in a const
called path
.
@data-js-transition-const-accounts In app.js
locate the lines that are responsible for reading and parsing JSON from the src/json/accounts.json
file. Copy and paste them to the new data.js
file below the require statements.
@data-js-transition-const-users In app.js
locate the lines that are responsible for reading and parsing JSON from the src/json/users.json
file. Copy and paste them to the new data.js
file below the accounts
const.
@data-js-write-json-function In data.js
below the account and user data lines create a function called writeJSON
. Hint: It is best to use ES6 arrow style function (=>).
@data-js-write-json-function-body In app.js
locate the lines in the transfer
post route function body that are responsible for writing JSON data to a file. Hint: there are two lines. Copy these lines to the body of the writeJSON
function in the data.js
file.
@data-js-export-data In data.js
use module.exports
to export an object containing the constants accounts
, users
, and the writeJSON
function.
@app-js-require-data-js Back In app.js
require data.js
and at the same time use object destructing to create three constants for accounts
, users
, and writeJSON
. Remove the lines in app.js
that create the accountData
, accounts
, userData
, and users
consts. accounts
, users
, and the writeJSON
function are now brought in by the require statement. Note: use relative paths to require the data module './data'
@app-js-call-write-json-transfer In app.js
locate the lines in the transfer
post route function body that are responsible for writing JSON data to a file and replace them with a call to the writeJSON()
function.
@app-js-call-write-json-payment In app.js
locate the lines in the payment
post route function body that are responsible for writing JSON data to a file and replace them with a call to the writeJSON()
function.
@routes-accounts-js-create-file Create a new file called accounts.js
in the directory src/routes/
(you will need to create this directory).
@routes-accounts-js-require-express In the new accounts.js
require the express framework and store a reference to it in a const
called express
. Next, call the express.Router()
function and store it in a const
called router
.
@routes-accounts-js-require-data In accounts.js
require data.js
and at the same time use object destructuring to create one constant called accounts
. Note: use relative paths to require the data module. Use '../data'
since it is one level up.
@routes-accounts-js-move-routes In app.js
locate the savings, checking, and credit get routes, cut and paste these routes in accounts.js
below the require statements. Now in accounts.js
update the routes to be part of the router by replacing app.get
with router.get
.
@routes-accounts-js-export-router In accounts.js
export the router
using the module.exports
syntax.
@app-use-account-routes In app.js
where your account routes used to be, call the use
function on app
with two arguments. The first argument should be /account
and the second is the accountRoutes
const.
Carlos comments: I believe the instruction to require
and create the accountRoutes
variable should come before this task. As is, the tests break because there is no accountRoutes
variable yet.
@routes-services-js-create-file Create a new file called services.js
in the directory src/routes/
.
@routes-services-js-require-express In the new services.js
require the express framework and store a reference to it in a const
called express
. Next, call the express.Router()
function and store it in a const
called router
.
@routes-services-js-require-data In services.js
require data.js
and at the same time use object destructing to create two constants called accounts
and writeJSON
. Note: use relative paths to require the data module. Use '../data'
since it is one level up.
@routes-services-js-move-routes In app.js
locate the transfer and payment post and get routes, cut and paste these routes to services.js
below the require statements. Now in services.js
update the routes to be part of the router by replacing app.get
with router.get
.
@routes-services-js-export-router In services.js
export the router
using the module.exports
syntax.
@app-require-account-routes Switch to app.js
and require the routes/accounts.js
file and store a reference to it in a const
called accountRoutes
.
@app-require-services-routes Switch to app.js
and require the routes/services.js
file and store a reference to it in a const
called servicesRoutes
.
@app-use-services-routes In app.js
where your account routes used to be, call the use
function on app
with two arguments. The first argument should be /services
and the second is the servicesRoutes
const.
@views-update-for-routes Since all URL paths have changed for accounts and services we need to change the following views:
- In
src/views/index.ejs
changehref="transfer"
tohref="/services/transfer"
- In
src/views/summary.ejs
changehref="<%= account.unique_name %>"
tohref="/account/<%= account.unique_name %>"
- In
src/views/transfer.ejs
changeaction="/transfer"
toaction="/services/transfer"
- In
src/views/payment.ejs
changeaction="/payment"
toaction="/services/payment"