Chandler-Track (rails/javascript)

Ryan Altimari
4 min readApr 19, 2021

--

For this project, I decided to draw on another side hobby. The last time I built something it was to track reading, now we are tracking candle making.

This was a very challenging application, and in the end (as always) a HUGE confidence boost once completed. I created a Rails API for my back-end, and used a combination of Javascript, HTML, and CSS for the front-end.

The run down

This was my first use of Javascript, and wow. What a powerful language, especially using Rails serving up all that beautiful data. This is also a single-page application, meaning everything happens on the same page without updating. Upon interaction, Javascript sends a request to the API to create, read, update, or delete the information from the database. It then uses the response information to update the DOM accordingly, without ever having to refresh the page! We can dive deeper into this, but first here's a peek at the visuals of the application.

There are spaces to track recipes, ingredients, notes, and total inventory. Let's take a look at the code, and I’ll show you some of the moving parts.

Models

My first move was to get my models and relationships up and running, I utilized a has-many relationship as per requirements. Here they are, including their relationships and attributes:

Recipe ( has_many :notes, has_many :ingredients )

  • t.string :name

Note ( belongs_to :recipe )

  • t.string :note_body
  • t.integer :recipe_id

Ingredient ( belongs_to :recipe )

  • t.string :name
  • t.string :measurement
  • t.float :amount
  • t.integer :recipe_id

InventoryItem

  • t.string :name
  • t.string :measurement
  • t.float :amount

And that's all, Nice and simple, and with this up and running we can let Javascript pick up the torch!

Front-end

My front end followed an object-oriented approach, using Javascript classes to encapsulate the data my back-end serves up. The main javascript object includes the logic and the adapter for that object takes care of the communication with the API. The classes are as follows: Ingredient, InventoryItem, Note, and Recipe along with the adapter files to match.

Here is an average object class:

class Ingredient{  static all = []  constructor(id, name, recipe_id, amount, measurement) {  this.id = id  this.name = name  this.measurement = measurement  this.recipe_id = recipe_id  this.amount = amount  this.element = document.createElement('div')  this.element.id = `ingredient-${this.id}`  Ingredient.all.push(this)  }
}

Here we have the name of the object, an empty array, and a constructor. Whenever a new ingredient is instantiated, “this” represents that ingredient itself. As you can see, it just works down the line and assigns those properties one after the other. After everything is properly set, I push this ingredient instance into the once empty Ingredient.all array

Hearing all of the events

Another awesome piece of the Javascript pie is the event listener. I really enjoyed getting to experiment and get better working with these. There are many throughout the application, they pull a lot of work since I am avoiding a page refresh. Here is an example of an event listener on an edit recipe form.

editRecipeForm.addEventListener("submit", function(e) {e.preventDefault()recipesAdapter.updateRec(recId)})

The first argument is targeting the submit button on our form, the second argument is an anonymous function, that passes the event (e) to the block. e.preventDefault() prevents the default action of the event target, which in this case is “submit”, this is what keeps the page from refreshing when a form gets submitted.

A majority of the code grabs onto HTML nodes and updates their content. The following method requests all of the notes from the database, and as long as the response contains at least one note, uses the information to attach them to the DOM.

addNotes() {let notesAdapter = new NotesAdapterlet notes = notesAdapter.fetchRecipeNotes(this.id)if (notes) {notes.map(note => this.addNoteToDom(note))}}

This is the function that does the work of putting the previously mentioned notes on the screen. First, it sets a variable to the correct DOM node, then it sets another variable to the element attribute of the note. Using those variables, the innerHTML can be set and finally, we can attach this to the DOM:

addNoteToDom(note) {const recNotesDiv = document.getElementsByClassName('recipe-notes-list')[0]let divElement = note.elementdivElement.innerHTML = `<div id="note-${note.id}" class="note-display-box"><h5 class="note-body">${note.note_body}<h5><div class="outer-2"><div class="inner-2"><label class="note-delete-btn" id="${note.id}">Delete</label></div></div></div><p>_______________________</p>`recNotesDiv.appendChild(divElement)}

I look forward to continuing my Javascript knowledge, as well as React experience. This project was an incredible struggle, which in turn means it was an invaluable learning experience.

--

--

Ryan Altimari

Full-stack Software Engineer sharing my journey in the industry. Join me as I tackle new concepts, projects, and languages one article at a time.