Assignment 1: JavaScript fundamentals
Written by Neel Kishnani, Jason Chuen, and MichaelDue Thu Apr 20 11:59pm PT
Submissions not accepted after Sat Apr 22 11:59pm PT.
Backstory: As you embark on your web development journey, you decide to set your sights on something we can all get behind: fixing Axess! And hey, those systems are probably so broken that we may as well start from scratch, right? But before you can take on course enrollments, you'll need a few building blocks first. Besides, as we come up on commencement, maybe you can build up your reputation as a reliable web developer by helping out with that.
This assignment will have you practice some fundamental JavaScript concepts and how to use them in the browser. You'll build a small app to simulate students declaring their major and graduating.
Learning goals
After completing this assignment, you will be able to
- Write functions and classes in JavaScript that manipulate various types and data structures,
- Create modules that import dependencies,
- Create a small HTML page with form elements, and
- Use JavaScript to respond to user events and read form controls.
Getting started
As with assignment 0, follow these steps to get started.
- Download the starter code and extract the .zip file's contents.
- Open your command line and navigate to the extracted
assign1
directory. - Run
npm install
. - Run
npm start
to start the web server. - Open
localhost:1930
in your web browser.
Deliverables: We've tried to use boxes like this one to highlight the specific tasks you will need to complete for the assignment. These boxes won't stand alone without the rest of the handout, but consider, for example, using them as a checklist to make sure you've done everything.
Part 1: Warmup exercises
The first part of the assignment consists of a few short warmup exercises, designed to give you practice with JavaScript data structures.
You will write your answers in warmups.js
, which has already been included in index.html
. We have provided a function testWarmups()
, callable from the console, to check your answers.
Task 1a: Using the debugger
The first task will give you practice using the debugger.
- To begin, please make sure you replace the TODO in the
import
line at the top ofwarmups.js
with your SUNetID (login name, not number). - The function
debugExercise
callscheckAnswer
, passing it a "secret code." Your task is to determine the correct code to pass. But sincecheckAnswer
is being imported fromdebugme.js
, located outside of yourassign1
folder, you won't be able to modify it (in order to, for example, add aconsole.log
). - Use the debugger to look at the JavaScript code for
checkAnswer
. Then, calldebugExercise
from the console and stop the function while it is running so you can inspect the values of the relevant variables to determine what you must pass to it. - Once you have the secret code, replace the TODO in the argument to
checkAnswer
. RerunningdebugExercise
should print a "Success!" message. - Finally, fill in the comment in
debugExercise
with a brief (1-2 sentence) explanation of your strategy: how did you enter the debugger, and where did you stop? How did you read the secret code?
Some notes for this task:
- The list of files in the Sources tab is split up by where the files are located. You'll find
debugme.js
underweb.stanford.edu
. - The secret code is a short string of random characters, different for each student. The script that generates it uses some advanced JavaScript features, so we've tucked it away in another file. You may look at the file if you'd like, but it's not necessary (and probably won't help you get the answer).
- This being the web and JavaScript, there are ways to "circumvent the process" and solve the task without the debugger. We ask that you avoid these unintended alternate strategies, both because we think the debugger is a valuable tool to have in your toolbox, and because we expect that solving the task using the debugger will be much easier. If, after following the process we've outlined, you want to think about how you might solve it a different way, feel free.
Task 1b: Working with data structures
For the rest of this assignment, you'll work with data for a few sample students and departments.
First, open data.js
to have a look at the provided data. The file exports a single JavaScript Object
with three entries:
students
is an Array of Objects, each object representing a student. A student object contains theirgivenName
, theirsurname
, and theirsunetid
. (Note: These aren't the CAs' actual SUNetIDs.) You can assume that SUNetIDs will be unique across all students in the array.depts
is an Array of Objects. Each object contains information about a department: itsname
(like "Computer Science") and itscode
(like "CS").units
is an Object mapping student SUNetIDs (keys) to the number of units that student has completed (values). You may assume that there will be one entry in the object for each student instudents
.
This data object has been imported into warmups.js
as the variable DATA
. Once you have reviewed the data, fill in the functions in warmups.js
, using DATA
, as follows:
firstNSunets
returns the SUNetIDs of the firstn
students (n
is a parameter to the function) in thestudents
array. You can assume there are at leastn
students.shortDeptCodes
returns an Object whose keys are department names and whose values are their corresponding codes. But only include a department in the result if its code is exactly two characters long.averageUnits
returns the average number of units completed by all students in the data. You can assume there will be at least one student, and you don't need to round or truncate the result.
Each function should be relatively short and will involve some form of iteration. Once you have written these functions, you can use testWarmups
(which is exposed to the console) to check that your answers match what we expect.
Note on style and style checking
To help identify some common style issues (and a few functionality issues too), we have includes a configuration file for ESLint.
If you are using VSCode and have installed the ESLint extension, these issues should be highlighted automatically as you code. (If you aren't seeing any highlights, you may have to restart VSCode after you do npm install
.)
If you are using something else, or would like to check for lint errors manually, you can run npm run lint
in your terminal (you'll have to do this in a different window than the one you ran npm start
in).
Note that ESLint won't catch all (or indeed most) style issues. We will give you specific style feedback on your assignment through grading. But having a clean lint run is a good first step!
Part 2: Modeling students and departments
For the remainder of the assignment, you will work with the various other .js
files in the public
folder. All of these files are imported by index.js
. We have included index.js
in the provided HTML.
Your next task is to implement two JavaScript classes which we will use to model students and departments in our app.
Task 2a: Student class
Implement the Student
class in student.js
, and export it as the default (and only) export. An instance of Student
will have the following properties (instance variables), which are all "public":
sunetid
: The student's SUNetIDgivenName
: The student's given namesurname
: The student's surnamedept
: The name of the department the student is declared in (a string), ornull
if the student has not declared yetunitsCompleted
: The number of units the student has completed (a number)isAlum
:true
if the student has graduated,false
if not
An instance has the following methods (with corresponding signatures):
constructor(sunetid, givenName, surname)
: Construct a newStudent
. The student's SUNetID, given name, and surname are as passed in. Students start undeclared, ungraduated, and with zero units completed.fullName()
: Return the student's full name, which is their given name, followed by a space, followed by their surname.addUnits(units)
: Increase the number of units the student has completed by the parameterunits
.toString()
: Return a string representing the student, which should be their given name, a space, their surname, another space, and their SUNetID in parentheses. (For example, "Michael Chang (mchang91)")canGraduate()
: Return a boolean indicating whether the student can graduate. A student can graduate if they have declared and have completed at least 180 units. If the student has already graduated, this method should throw anError
with a descriptive message.
Task 2b: Department class
Now write the Department
class and export it as the default export in dept.js
. An instance of Department
has the following "public" properties:
name
: The full name of the departmentcode
: The department codestudents
: An array of the current (non-alum) students who have declared under this department
Implement the following methods:
constructor(name, code)
: Construct a new department with the name and code passed in. Departments start with no current students.toString()
: Return the string representation of the department, which is just the department's name.declare(student)
: Declare the passed-inStudent
instance under this department, updating the student's and department's instance variables accordingly. If the student is already declared under this department, this method should return without doing anything. But if the student is already declared under a different department, this method should throw anError
with a descriptive error message.graduate()
: Check if each current student in the department can graduate; if they can, mark them as an alum and remove them from the list of current students. Return an array of the just-graduatedStudent
instances.
Some notes on these tasks:
- You may add additional instance variables and methods to these classes if you wish, but you are not required to.
- It is a good practice to reuse existing functionality where possible. For example, notice how a
Student
'stoString
contains their full name. - You may assume that a client of these classes will not modify the instance variables in a way that makes the instance inconsistent. For example, a client will not directly set a student's
dept
without going throughdeclare
. - You aren't required to do any error checking beyond what is described here.
- Review the end of lecture 3 for the syntax for throwing
Error
s. Your errors should generate exceptions that can be caught and handled by the client of your class (which you will do in the next part).
Part 3: The App class
Finally, you'll put this app together by implementing the App
class (in app.js
).
Task 3a: Console interface
An instance of the App
class is constructed by index.js
when the page loads. App
has the following instance variables:
students
: An Object mapping students' SUNetIDs (keys) to correspondingStudent
instances.depts
: An Object mapping department codes (keys) to correspondingDepartment
instances.
The constructor has been started for you; you will add to it in the next task. First, implement the following methods:
loadData(data)
: Takes an object with the three keys from the sample data (students
,depts
, andunits
; see task 1b) and populates the app's instance variables:- First, reset the
students
anddepts
instance variables to be empty. This allowsloadData
to "reset" the app. - Populate the
students
map with newStudent
instances, using thestudents
data. - Update the number of units each student has completed via the
units
data. - Populate the
depts
map with newDepartment
instances, based on the passed-in data.
- First, reset the
declare(sunetid, deptCode)
: Takes a SUNetID and department code (both strings) and tries to declare that student under that department, then returns the (updated)Student
instance. If the SUNetID or department code isn't known to the app, throw anError
with a descriptive message. (If callingdeclare
on theDepartment
causes an error, don't handle it here.)graduate(deptCode)
: Make the department specified by the passed-in code try to graduate its students, returning the list of graduates (Student
instances). If the department code doesn't match a known department, throw anError
with a descriptive message.
You will also need to add imports for Student
and Department
.
Testing
After completing these methods, you will be able to test your app from end to end:
- The
app
instance is exposed to the console. You can inspect its instance variables and call its methods. - For example, you could call
app.declare(“mchang91", "CS")
orapp.graduate(“BOGUS")
. (Note thatloadData
is already called for you inindex.js
.) - The function
testApp
, exposed to the console, will attempt to test a variety of methods from part 2 and this task. If anything doesn't match, it will print out an error message. These tests aren't comprehensive, and we encourage you to add some more tests of your own.
Task 3b: User interaction on the page
Your final task of this assignment is to expose the declare
and graduate
methods you just wrote to a user of the web page:
- Edit the HTML to add a form with two inputs, one for SUNetID and one for department code. These inputs should be labeled so they are identifiable, and make sure to give them
id
s so you can reference them in JavaScript. - Add two buttons to the form, one for "Declare" and one for "Graduate".
- In
App
, implement the functionality for these buttons: - When the user types in a SUNetID and department code and clicks "Declare", call your app's
declare
method with the entered values. - If the declaration is successful, display an
alert
informing the user, including the student's name and the department name. For example, "Michael Chang (mchang91) declared Computer Science!" - If the declaration fails, display an
alert
with the error message. (Note: It's not enough for the error to be printed to the console, since users won't think to look there. You must catch the error and present it viaalert
.) - Similarly, implement the "Graduate" button. This button ignores the entered SUNetID. Upon success, display an
alert
listing the students who graduated. For example,Graduates: Neel Kishnani (neelk) Jason Chuen (jahchuen)
(You can use\n
to add a newline inalert
messages.)
Some notes on this task:
- You do not have to lay out the form controls in any particular way and are not expected to add any styling. As an example, here's how we laid them out:
- You will need to add additional methods to
App
and are free to add instance variables as well. But you should mark them as "private" by prefixing them with an underscore (_). - Recall from lecture 4 that you can access the forms on a web page using
document.forms
, and you can reference the controls of a form using theirid
or a property of the form. - Remember that a
<button>l;
inside of a<form>
defaults to havingtype="submit"
, meaning it will refresh the page when clicked. You'll want to change its type tobutton
to avoid this. - Don't forget to
bind
your event handlers! As seen in lecture 4, it is best practice to do this in the constructor (and this is preferred over creating functions on the fly.)
Submitting
When you are finished, please remember to delete your node_modules
folder, and then submit your assign2
folder to Paperless.