JavaScript/Rails Project — Using Classes to Create Highly Organized Code

Harry Wilson
4 min readJul 23, 2020

--

This week I completed my first full-stack Rails backend/JavaScript frontend single page application. This is by far my biggest project to date with over 100 files, 8 models on the backend and the use of polymorphic associations. I chose the image below to illustrate how easy things can be to find when you organize them. In this project, I used classes and Object Oriented JavaScript to keep my code incredibly well organized and found that fixing bugs was much easier than it had ever been as I could easily trace the source of the error in my code.

Photo by Edgar Chaparro on Unsplash

As this was my first major project that used JavaScript, I will focus in this post on the frontend structure of my application only. In order to organize my code in a clean way, I used classes, one for each model as well as an api class which I used to make calls to the backend. Finally, I had a main app.js file which was used to grab hold of elements as well as add some event listeners.

Having classes allowed me to keep track of key information, such as the current player, the current round, players’ scores and much more. The flow of my application was surprisingly easy to follow for a card game with 1000s of lines of code.

class API {    static baseURL = 'http://localhost:3000';    static get(url) {        return fetch(this.baseURL + url).then(function (response) {          if (response.status !== 200) {            throw new Error(response.statusText);          }          return response.json();     });    }

My API class contained a static class level method for each of the standard HTTP request verbs (get, post, patch, delete) and this allowed me to easily make fetch requests to my database from anywhere within my other classes. I could then use the JSON data I received (as I set that as the return value) to call other methods or store data within my frontend. I specifically designed my API class methods to take in a url endpoint and data so that the methods were as clean as possible and could be called from anywhere.

createGame(params) {    API.post('/games', params).then((data) =>        this.removeDuplicatePlayers(data)    );}

As you can see in the code snippet above, I could easily use data (in this case, data entered into the initial start game form) and make requests to my API. I then use the data to call other methods within the same class.

Throughout this project, a difficulty I encountered was trying to find a way to be able to take this data and call it on methods found in other classes. Obviously with class methods this is easy, but for me it was very important to be able to call certain things only on the current player or current game from within other classes such as the deck or round class.

I solved this issue by adding static values to each of my classes named ‘current(insert class name here)’.

static currentGame;save() {    Game.currentGame = this;}

I then created save() methods as instance methods which I could then call in order to set that classes currentGame value to that particular instance. This came in very handy throughout my application.

class Game {    constructor(playerOrder, id) {        this.playerOrder = playerOrder;        this.id = id;    }

In my class constructor I could set attributes which I knew I would need. In the case of the game instance, I wanted to always be able to keep track of the playerOrder so that when I finished a turn or a round (in my dropzone and round classes respectively) I could access the playerOrder in order to see who would go next and therefore which div I would need to make visible to show that player their hand. By adding the playerOrder value and then calling the save() method on that particular instance, I was able to access the playerOrder anywhere in my 8 classes by calling the following:

Game.currentGame.playerOrder

I did the same thing with the currentRound, I also kept track of all the players details such as their id’s, whether or not they had drawn a card, placed any sets and much more.

endTurn() {    let nextPlayerIndex = 0;    const currentPlayerIndex = Game.currentGame.playerOrder.indexOf(        Round.currentRound.currentPlayer    );    if (currentPlayerIndex < Game.currentGame.playerOrder.length - 1   ) {        nextPlayerIndex = currentPlayerIndex + 1;    }    const screen = document.getElementById(        `player${Round.currentRound.currentPlayer.id}`    );    screen.style.display = 'none';    Round.currentRound.currentPlayer =        Game.currentGame.playerOrder[nextPlayerIndex];    const betweenTurns = document.querySelector('.betweenTurns');    betweenTurns.style.display = 'flex';    const betweenTurnsMessage =   document.querySelector('.betweenTurnsMessage');    betweenTurnsMessage.textContent =  `${Round.currentRound.currentPlayer.name}'s Turn Will Begin  Shortly`;    setTimeout(Game.currentGame.turnInfoUpdate, 5000);}

As you can see in the endTurn() method found in my Round class. I take advantage of checking the playerOrder to see which position my Round.currentRound.currentPlayer in found within that order. Using the index position of that player, I am able to check which player is next. I then grab hold of their hand div by using their id information I have saved, I set the current (soon to be previous) player’s div to display none, I display the between turn message which states that (new current player name)’s Turn will begin shortly and then I run a function to update the screen using the data I have set.

As you can see, classes can be an extremely powerful tool if used and written correctly. I made use of classes to produce extremely organized code which made it possible for 8 (9 including my API class) classes to work together, making very few API calls thanks to my save() methods. All my key data was available to me at any point in any of my classes and bugs were extremely easy to trace due to the organized manner in which I setup my code.

--

--