Javascript and Rails Single Page Application ("SPA")
Tags: javascript, rails, codenewbie, css
Published: 2020-10-06 17:26:16

<center> GUESS WHAT!? </center>

I built a single page application using vanilla Javascript("JS") as the frontend and Ruby on Rails("Rails") as the backend. 

{% youtube yA5mG-l8TuA %}

<center>
<a href="https://github.com/SincerelyBrittany/trackYourWorkout">Github Link</a>
<a href="https://youtu.be/yA5mG-l8TuA">Youtube Demo</a>
<i> <a href=""> </a>Live Site - Coming Soon </i>
</center>

<center><h1> Introduction </h1></center>

I just want to say that this project was NOT easy, in fact this project was one of the hardest I had, in my opinion. With Rails, everything seemed so simple and organized, but then Javascript came and showed me that there are no RULES 😩😂! Learning Javascript is no easy task and I have reasons to believe that no one ever truly knows Javascript or all that it is capable of. I constantly found myself going to different platforms to learn more and although it was hard at times, I enjoyed every minute of it. I learned about Javascript variables, functions, data structures, hoisting, closures, class syntax, and much much more.

I recommend this course if you are learning Javascript, it helped me a lot. <a href="https://www.udemy.com/course/understand-javascript/learn/lecture/2237466#announcements"> Javascript: Understanding The Weird Parts </a>

<center><h1> Planning </h1></center>

First I had to come up with an idea for my project. I had to plan out my database. I created the following flow chart on Draw.io:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/wzzyzflyxkspu8e562k5.jpg)

I knew that I wanted my application to have many users and that each user would have many workouts. However, I wanted to be able to have multiple workouts under the same date and my instructor recommended a join table for that, he is a smart man. Next step was to put my plans into action. 

<center><h1> Using Rails as an API </h1></center>

The main difference between my previous Ruby on Rails application and this one is that I was only using it as an API. That meant that I had to run the following code to get my <a href="https://guides.rubyonrails.org/v5.0/api_app.html"> Ruby on Rails API</a> up and running: 

```
$ rails new my_api --api
```

Once I got rails api up, I added a few gems that I knew I needed into my Gemfile. The first one was ``gem 'rack-cors'``, which is a a gem that allows web applications to make cross domain AJAX calls. More information on how rack-cors works and how to implement it can be found <a href="https://github.com/cyu/rack-cors">here</a>.

Once my gems were all set up, I created my migrations, created seeded data, and set up my RESTful routes. Another difference between a project built completely using Ruby on Rails versus when you are using it as your server is that you need to return the data as JSON. For example, my index method in my Users Controller would look like this:

```
class UsersController < ApplicationController
 def index
        user = User.all
        render json: user
    end
end
```

I have to render the json, so that when I run my server and visit http://localhost:3000/users in my browser, I will see data similar to this: 

```
{
    "users": [
      {
        "name": 1,
        "name": "Harry Potter",
      },
      {
        "id": 2,
        "name": "Courage The Cowardly Dog"
      }
    ]
  }
```

The json data is now accessible to me and I can now make a fetch request to my backend.

<center><h1> Making <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch">Fetch</a> Requests To My Database </h1></center>

In order to get the information from my backend I had to make a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch">fetch</a> request. First I made sure my server was running by keeping a terminal open in my backend, ``rails s``, so that I could access the json data. Then, I created a file to make the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch">fetch</a> request to my join user_workouts table:

```
class WorkoutsAdapter {
    getWorkouts(){
        return fetch(`http://localhost:3000/api/v1/user_workouts`).then(res => res.json()
        )
    }
```

<center><h1> Iterating and Rendering Onto The Page </h1>
<h3> My biggest challenge </h3></center>
Once the information is returned, I wanted to iterate through the information and sort it by date, I was able to do that with the following code:

```
class Workouts{
    constructor(){
        this.workouts = {}
        this.adapter = new WorkoutsAdapter()
        this.fetchAndLoadWorkouts()  
    }
   fetchAndLoadWorkouts(){
        this.adapter.getWorkouts().then(workouts =>{
            workouts.forEach(workout => {
                if (this.workouts[workout.date]) {
                  this.workouts[workout.date].push(new Workout(workout))
                } else {
                    this.workouts = {...this.workouts, [workout.date]: [new Workout(workout)]}
                }
      
            })
        })
        .then(() =>{
            this.render()
        })
    }
```
The above code makes a request to the WorkoutsAdapter class and fetchAndLoadWorkouts() function, which is essentially the fetch request. The information received is the array of workouts. Then ``for_each`` workout it checks to see if the date exist in the ``this.workouts`` object. If the date does exist then it will push the workout into that date. If the date does not exist, then it will create a workout with that date as the key. Once it is done iterating through the data, it will go to the ``render()`` function that I created. 

Below is my render function:

```
    render(){
   this.workoutContainer = document.getElementById('workout-container')
        const workoutString = Object.keys(this.workouts).map((date) => {
           return `<button type="button" class="collapsible">${date}</button><div id="all-workouts">
            ${this.workouts[date].map((work) => work.renderHTML()).join(' ')}</div>`
        }).join(' ')
        this.workoutContainer.innerHTML = `${workoutString}`
    }
```

I was able to successfully render onto the page by using the  document.getElementById('workout-container') to select the element/div in the HTML. Then, using the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys"> Object.keys</a> method on the ``this.workouts`` object, I mapped the data by the date and returned the dates in a button. Then I mapped the workouts by the date and placed them in a div and the the workouts successfully rendered onto the page underneath the correct date for each user. 

This code was very challenging, but I was so happy when it was accomplished. Feel free to take a look at my project, I welcome all feedback and contributions. 

As always, thanks for reading!

Sincerely, 
Brittany

Song of the day:

{% soundcloud https://soundcloud.com/user-670217828-690849668/raahiem-peak %}