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.
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.
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.
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.
- The following events occur in the sequence to fetch the categories list and update the DOM.
- The
getCategories
function fires off an AJAX request to the index route in /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.
5. Nested inside the div class=”items”
element are elements containing item information with attribute data-item-id
set to the categories item.id.
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).
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.
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.
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.