Single Page Application’s (SPA) are prevalent throughout modern web application development. The benefit of a SPA is that the page does not require reloading. This allows the user to interact with the application without virtually any downtime. When implemented correctly, the browser will always have content displayed even if a user click’s on a link on the page. Only a portion of the page will get rendered which provides seamless user interaction with minimal wait time.

As par of the Flatiron School curriculum, I created a SPA using a Rails backend API combined with a vanilla Javascript frontend. The frontend utilizes JS object orientation (OO) in order to organize the code in a useful and structured way. The application I built is called “Gastropoda” and is a simple literary magazine designed to focus on the content of the stories.

Image for post
Image for post
The main “page” where the most recent stories are displayed in a manual slideshow.

The main struggle I went through when building this was related to the context for which I was coding in. Since this was my first time working with object oriented JS, I only knew of the this keyword. I am familiar with “self” in Ruby, and while this is similar to “self”, it is different enough that it made avoiding JS errors challenging.

I defaulted to using this in the beginning of the build. Once errors popped up in the console, I made a change by creating a variable at the top of the method and assigning it to this , like const self = this. While this works, it proved to be unnecessary in every method that I did this in. I settled on replacing all of these unnecessary variables by implementing arrow functions instead of classic function declarations.

The beauty of arrow functions (if your needs are the same as mine) is that they do not bind their own this but simply inherit from the scope of the parent. So if you do not need access to the content for which you are calling this on, arrow functions may be useful.

Image for post
Image for post
By using an arrow function on line 159, every use of “this” afterwards is in the correct context.

Another implementation I used to control the context of functions is by using bind. In the method below, event listeners are set and callback functions are passed in as arguments. Since the callback functions that are passed in are also methods inside this class, the context of these functions would be whatever comes prior to the .addEventListener. To address this issue, I bind this to the callback function in order to assign the context.

Image for post
Image for post
These three event listeners require .bind(this) in order to function properly.

Another issue that challenged me for awhile was how to pass in an argument to a callback function when you are using .bind. The last two lines of the method below shows that the argument goes in the .bind parenthesis, but comes after the first argument of this.

Binding the context to callback functions while passing in an argument.

I also spent a considerable amount of time avoiding making new fetch requests if it wasn’t absolutely necessary. When the app is initially run, the class method Entry.loadEntries() is invoked. This class is where the majority of the JS code lives. This class has a static method named allEntries that initially points to an empty array. This array is populated with entry objects when loadEntries() is called which pushes these new objects into the allEntries as part of the constructor for instances of Entry. So from the start, a get fetch request to the backend is initialized which is the only time a get request for entries is utilized . I configured the data using a serializer so that everything the fetch request returned was all the data I needed for the app. This includes the comment objects associated with an entry (an entry has many comments and a comment belongs to an entry). Because of this, a fetch request for an individual entry is unnecessary since all of the data is turned into Javascript objects and held in an accessible array.

Image for post
Image for post
Every new instance of Entry that is created is stored as an object in the static allEntries array.
Image for post
Image for post
The data in the API is structured in an accessible way and includes all data necessary utilized in the app.
Image for post
Image for post
A new instance of the Entry class is created on the fly and immediately pushed to the allEntries array. This allows the objects returned from the backend to be populated to the front end as objects. Now, additional fetch requests aren’t necessary to display a particular comment or story.

The Final SPA

Image for post
Image for post
The event listener for “Stories” renders the main content below the navigation bar.
Image for post
Image for post
An individual story with a social media type icon to show the number of “likes”.
Image for post
Image for post
Comments can be viewed and made at the bottom of the story.
Image for post
Image for post
User’s can submit their story and it will be loaded into the database if validations are met. If this app were live, the story would be reviewed prior to posting. But for the sake of this project, the new story will appear in the “Stories” view instantaneously.

The Github repository for this application can be found at the link below. Feel free to clone the repo and run the app in your browser to play around with it. Any comments or questions are greatly appreciated!
https://github.com/dougschallmoser/gastropoda-js-app

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store