Inventory-Tracker Rails api JavaScript frontend

The fourth software engineering project requires the app to use Javascript frontend with a Rails API backend. Client/server interaction must be handled asynchronously in JSON format. The Rails backend needs to have a resource with a has-many relationship and have at least 3 AJAX calls (at least two of Create, Read, Update, and Delete).

My JS + Rails API project is an Inventory tracking system that keeps a record of the items you own and where they are located. The object model relationship is a category has many items in its record. A screen shot is shown below.

Inventory Tracker App

The project is structured such that the front-end is decoupled from the back-end. This partition allows me to run both ends at the same time, with Rails sever running on localhost:3000 while the front-end Javascript and HTML/CSS run separately on the browser. This partition also allows both ends to be deployed together or separately.

CORS

I uncommented the line gem ‘rack-cors’ to allow cross-origin resource sharing (CORS). CORS is a mechanism that allows restricted resources on a web page to be requested from another domain outside the server’s domain. It provides browsers a way to request remote URLs only when they have permission and allows servers to reject requests from unknown origins.

For this project, the config/initializers/cors.rb is configured to allow requests from all origins and to allow [:get, :post, :patch, :delete] requests to the API.

Allow requests from all origins

Serializer

I added the gem 'active_model_serializers' to serialize the data and select only the information I want to display. It also provides access to the has_many relationship with a single request.

Category Serializer
http://localhost:3000/categories

Controller

All categories are rendered in the form of JSON. The category_params permits the parameters :title and :description, which must be included in the POST and PATCH requests made with Javascripts fetch.

class CategoriesController < ApplicationController
before_action :find_category, only: [:show, :edit, :update, :destroy]


def index
@categories = Category.all.order('title ASC')
render json: @categories
end

def show
@category = Category.find(params[:id])
render json: @category, status: 200
end

def create
@category = Category.create(category_params)
render json: @category, status: 200
end


def update
@category.update(category_params)
if @category.save
render json: @category, status: 200
else
render json: { errors: @category.errors.full_messages }, status: :unprocessible_entity
end
end

def destroy
category = Category.find_by(id: params[:id])
category.destroy
render json: category
 end


private

def category_params
params.require(:category).permit(:title, :description)
end

def find_category
@category = Category.find(params[:id])
end
end

Index.html

The index.html is set up to have div tags for the new category form (id="category-form") and for the list of categories (id="categories-list"). These divs are initially empty but are populated with the JSON response elements.

As JSON data is fetched, the HTML DOM is appended with elements to display the category and item properties. The basic structure is shown below.

      <div class="card" id="category-form">
        <! -- New category form here -->
      </div>      <div class="card" data-category-id="1">
        <!-- View Inventory button here -->
        <!-- Edit Category button here -->        
        <!-- Delete Category button here -->
 
        <!-- Category info -->        

        <div class="items" style="display: block;">
           <div class="card" data-item-id="1">
             <!-- Item info -->
             
             <!-- Delete Item button here -->
           </div>           <!-- Add Item button here -->
        </div>
      </div>

Fetching Categories

The DOMContentLoaded event fires when the initial HTML document has been completely loaded and parsed so that we can fetch the list of categories and render a new category form.

DOMContentLoaded Event
  1. The following events occur in the sequence to fetch the categories list and update the DOM.
  2. The getCategories function fires off an AJAX request to the index route in /categories.
AJAX Request to Fetch Categories

3. The JSON response data is used to create new Category objects in renderCategoriesHtml. Each categories properties are appended to the id=”categories-list element. An element with an attribute called data-category-id is created with the category.id as shown below.

4. Nested inside each individual categories div data-category-id element is a div class=”items” element.

div class=”items”

5. Nested inside the div class=”items” element are elements containing item information with attribute data-item-id set to the categories item.id.

data-item-id attribute

Click Event Listeners

After the DOM is updated with the list of categories and their items, the addCategoriesClickListeners function is called. This function sets up the action handlers as a response to the user clicking on the edit button (to update the categories information), and the delete button (to remove the category).

Categories Click Listeners

Similarly, the addItemsClickListeners sets up the event handlers as a response to the user’s clicks on the View Inventory, New Item and Delete Item buttons.

Items Click Listeners

Adding a New Category

The new category form is rendered as shown below. When the submit button is clicked, createCategory is called. The return false cancels the default submit action. Instead, the form submission is handled via JavaScript and not the URL.

New Category Form

The createCategory function takes the values entered by the user in the new category form, creates a category object and attaches the stringified object to the body of the POST request.

Calling fetch returns a promise. We can then wait for the promise to resolve by passing a handler with the then() method of the promise. The handler receives the return value of the fetch promise, which is the Response object.

Updating a Category

A categories information can be updated when the Edit button is clicked. The editCategory function first fetches the selected categories’ information and pre-fills the edit category form with the returned information. The user can modify the field values with new information.

When the submit button is clicked, a PATCH is issued with the updated information as an object attached to the body of the fetch request.

Deleting a Category

The deleteCategory event action handler issues a DELETE of the selected category and removes the HTML element with the categories data-category-id attribute.

Adding and Deleting Items

Rendering item information in the DOM is similar to the categories added and deleted functions.

Source Code

https://github.com/DesterStorm/Inventory-Tracker