My JavaScript Project — StudyDecks
My fourth project for Flatiron School is a single-page application called StudyDecks. I am going to discuss what was required of this project, how the app works, and some challenges I experienced creating this app. The goal of the app is to provide a study tool for users where they have decks (topics a user is studying) and each deck has flashcards. Each flashcard can be flipped over, to provide the element of quizzing yourself.
This project had a few requirements, the first being the application must have a front-end built with HTML, CSS, and JavaScript that interacts with an API built using Rails. Everything that happens when the user interacts with the program needs to happen without the page ever refreshing or redirecting. Furthermore, the interactions between the client and the server must be handled asynchronously through AJAX calls and the data sent from the API would be in JSON format. The application needs to make at least 3 AJAX calls and 2 of them need to be from the 4 CRUD actions (Created, Read, Update, Delete). Additionally, the JavaScript in the project must use Object-Oriented JS (classes) to encapsulate related data and behavior. Our Rails API must have a resource with at least 1 has-many relationship.
AJAX TANGENT
AJAX — short for asynchronous JavaScript and XML. It is the general way we make requests to the server without reloading the web page and then we work with the data we receive. These calls are highly beneficial because it allows your program to continue running instead of waiting for all the data to come in and then moving on to the next lines of code.
Imagine you are in the kitchen of a restaurant. You are getting continuous orders for food to be made (akin to a program making requests for data). These orders are put in a queue. However, if you wait for the first meal to be fully made before starting on the next one, your customers would be waiting forever to get their food, especially if someone ordered something much less complex or time-consuming to make than another person. Instead, as orders come in, you start to process them. When a meal is fully prepared, you send them with the waiter back to the customer (request completed). However, that meal that is prepared could have been the fifth order that came in while the first one is still being made. The huge benefit is you did not slow your restaurant down because you were waiting for each meal to be made before moving on to the next. In a similar fashion, with AJAX calls, the program essentially puts that request in a side queue, continues running the next lines of code, and when that request completes, it just reinserts what it returned into the program.
How My App Works
- The HTML page loads up, but only the HTML for the login shows. If a new user wants to sign up, they can click on the link.
- Instead of redirecting to a signup page, the “link” has a JS Event Listener which hides the HTML containing the login form and unhides the HTML for the signup form. Vice versa exists from signup page to login page
- When logging in, the username and password provided are sent to the Rails backend via a fetch request. Rails will authenticate the password provided.
- After logging in or signing up, instead of redirecting, on the frontend, the DOM elements update by hiding the HTML for login/signup and unhiding the main HTML of the app
- Then a fetch request is automatically sent to the backend to retrieve any decks the user has previously created. If there are decks, they will be rendered onto the DOM. Once a deck is clicked on, the first flashcard in the deck will appear on the screen
- When a user creates a new deck or a new flashcard, this information is sent to the backend to be added
- With new information, a page might refresh to then reflect the new information. However, with this single page application, we want the user to see the changes without the page refreshing
- Thus, variables were created to maintain the state while the app is being used. These variables keep track of the current deck, the flashcards in the deck, and the current flashcard being viewed
- By maintaining state, when a new deck or flashcard is created, while the backend is updated, the same information can be used to update the state variables.
- This allows the DOM to update with the new elements without having to make another fetch request or refresh.
- After, a user can then edit or delete a flashcard. They can also go between flashcards using the previous and next buttons.
- Finally, they can flip the flashcard (animation included!) over to go between the front and back of the flashcard to quiz themselves.
Challenges
My first challenge was how I could reflect DOM changes when a user created/deleted a deck or created/edited/deleted a flashcard. I struggled initially with how I could set it up. For example, if I was on flashcard #2 of 5 in a deck and I decided to add a new flashcard, I would want to add that flashcard to the end of the deck and also show it upon creation. Furthermore, I could not have the page refresh. My solution was to maintain state. If I tracked which deck the user was on, and each time a deck was clicked, this variable would update. Then, it would make a request for all flashcards and filter by the known current deck. Plus, it stored all the flashcards in a variable for the current flashcards of the deck. This allowed me a place on the frontend to access flashcards and add/edit/delete flashcards that reflected the same changes on the backend without needing to make a new fetch request or have the page refresh.
Another challenge I faced while building this app was rendering my errors. I was having difficulty having my errors render to the page when either login/signup information was incorrect or a user did not fill out both sides of a flashcard. I had set up my API to render error messaging in JSON format if something failed validation. When making a fetch request, I was attempting to use .catch() at the end of the function to get those error messages, however, my messages were never coming through when I did a console.log() in the catch function. Using my Google skills, after some time, I finally found out what the issue was. On my backend, when validation failed, the JSON rendered came with a status (i.e 200 is okay, 500 is internal service error, etc). All of the statuses I had set up were actually valid responses to a request to a server, thus they were not bad requests. If they were bad requests, this would direct to the catch function. Since they were valid, the response would still execute in my then function. Thus, I just had to add some conditional logic to my then function and BAM, my errors were now rendering.
My final challenge was once I had a working app, I wanted to add a flip animation to the flashcard to provide a better user experience. I ended up using JQuery that toggled a flip class that would become active when my flip button was clicked. The flip action used CSS that transformed an element to rotate 180 degrees. At first, I was ecstatic because I got the flip action to work. However, as I had people test my app, a common error that occurred was if a user clicked another one of the buttons after flipping to the back of the flashcard, the next flashcard or flashcard form that was rendered would appear backward. This also happened when clicking on a deck. Each time I thought I fixed it, another place in the code needed to be patched. For the time being, my solution to this recurring bug was to add conditional logic that checked if the flashcard on screen was on the “back” of the flashcard. If it was, then any place in my code that updated the DOM would flip the container housing the flashcard back before rendering the new element to the DOM.
Future Steps
My next goal for this project moving forward is to create a navigation pane that allows a user to scroll through all the flashcards and immediately navigate to a particular flashcard.
I had a lot of fun building out this application. If you want to use it for yourself, you can check out the deployed version at https://study-decks.herokuapp.com/. If you want to see my code, see the repo here.
The Journey Continues!