Want to Make Your Application A User-Friendly Speed Demon? Just Add JavaScript!

By it’s nature, each time a webpage is loaded, the browser is going to try to load all components linked in the code, including scripts, CSS, graphics, video, etc. (unless a user has previously visited that webpage and has had some elements of its data cached). Even if some of the data is cached, it will take the browser a second or two to dig up those resources and display them to the user. But what if your site was fairly static with the exception of some data elements. If you were to add a record, or make an update, does it really make sense to reload the whole page? In many cases, the answer is probably “No”. Alternatively, we can utilize quick JavaScript-based AJAX (Asynchronous JavaScript and XML) calls to send our data to the server, retrieve a result, and update the current page accordingly. All without having to reload the page. This results in better utilization of resources and provides a better user experience.

Currently, the most effective format of handling data in AJAX calls is through JSON (JavaScript Object Notation). This places data in a hash-like format with key/value pairs which can be read by the browser, calling on or iterating through a specific node. Rails 4 can render JSON data with the addition of the active_model_serializers gem to the application while Rails 5 has this functionality built-in. Subsequently, when calling a controller action via AJAX, rather than having the controller action render an HTML/ERB page, it can render/return JSON data to the browser which it is then able to digest.

Making AJAX Calls Using jQuery

As a portfolio project for Flatiron’s curriculum on JavaScript and jQuery, I took the Entertainment Management System (EMS) that I had previously developed and implemented AJAX calls to quickly display and process certain pieces of information- primarily for the Performance, Contract, and Document models. For the purposes of this project, jQuery was utilized to execute the AJAX calls to perform GETs, POSTs, PATCHs, and DELETEs against the Rails application. jQuery provides a library of functions that abstracts away much of the lower level JavaScript and allows the programmer to succinctly issue a command (like a GET via $.get()) and describe what to do with the results.

Updates To My Application

Throughout the application, I rely on the Performance model to assist in displaying lists of upcoming and past performances, for specific Acts, specific Venues, and in summary across both of these models. Previously, a series of Rails partials were rendered to display this information, with one partial rendered multiple times to display each Performance line item. In this jQuery version, a single call is made to the Rails controller to retrieve the appropriate listing of performances and subsequently, each performance record is “pushed” into either the “Upcoming Performance” section or “Past Performance” section by comparing the performance date attribute to the present date. Additionally, in displaying the performance detail for a selected Performance, an AJAX call is made to retrieve and then display this information as well.

More interesting and visible changes can be noticed when looking at the Contract show page. First, both the Contract Detail and listing of Documents are rendered through an AJAX call to the controller, which in turn renders JSON via a specified serializer, and is interpreted back by the browser to populate the Contract fields as well as the listing of documents (similar to the Performance listing). However, in the Documents section, when a privileged user is adding a new document or modifying an existing one, a Modal window is displayed, providing the fields for the Document Name, Description, and URL location rather than navigating to a separate page. For a new document, once the data is populated and submitted, the modal disappears and a new Document line item is instantly added to the Contract page. Additionally, if a user were to Edit an existing Document, rather than the browser navigating to a Edit page, a Modal window with the same 3 text fields are also displayed and populated with the current values. Once a user updates the applicable fields and clicks Update, the Contract page is updated instantly.

In the Approvals section, if the Contract is pending the User’s response (Approve or Reject), the application will display corresponding buttons to provide this response. Previously, pressing one of these buttons resulted in a form submission where the result is submitted to the server, processed, and returned to the user following a page refresh. Using jQuery, I was able to instantly submit the result, retrieve the response, and update the status the timestamp and remove the buttons for the approval, resulting in the same output as if the page were refreshed or navigated to from another page.

$.ajax({
      url: "/approvals/" + answer,
      type: 'POST',
      dataType: 'JSON',
      data: {id: event.currentTarget.getAttribute("data-approval-id")}
    }).done(function(response, stat){
      approvalObject = new Approval(response);
      $(`[data-approval-id="${approvalObject.id}"] > td#date_answered`).text(approvalObject.fmatDate(approvalObject.date_answered));
      $(`[data-approval-id="${approvalObject.id}"] > td#status`).text(approvalObject.status);
      $(`[data-approval-id="${approvalObject.id}"] > td#buttons`).text("")
    }).error(function() {
      alert("There was an error processing your response submission. Please try again.")
    });

Creating JavaScript Objects from JSON

As previously mentioned, each AJAX call returned the results in JSON notation. While it is possible to access and work with this information directly from a variable which was assigned to perform the AJAX call and in turn store the results, in keeping with Object Oriented paradigms (of which a Performance, Contract, Document, Approval, etc. is), it is beneficial to take this returned data and turn the result(s) into JS objects. This  not only allows a developer to maintain consistency and reuse of the data structure, it also allows for the creation of Object Methods and Prototypes. Methods and Prototypes encapsulate JS functions within an Object that to be able to perform operations on an instance of an object. While Object Method(s) are instantiated each time a new object is created, a Prototype is instantiated only once for a given Object model. In the case of this project, I created Prototypes that assisted in formatting the date and time of data attributes in the various models.

Approval.prototype.fmatDate = function(dt) {
  d = Date.parse(dt);
  d = new Date(d);
  var dayShortNames = ["Sun", "Mon", "Tues", "Weds", "Thurs", "Fri", "Sat"];

  return dayShortNames[d.getUTCDay()] + " " +(d.getUTCMonth()+1)+"/"+d.getUTCDate()+"/"+d.getUTCFullYear();
}

A Few Challenges and Lessons Learned

As I was developing this application there were a few challenges I ran into. First, the non-JS version of the application relied heavily on use of Pundit, a ruby gem to assist in controlling the rights a user has to resources and the actions that can be performed on those resources. I had used Pundit policies to determine whether to display buttons or links to allow a user to Create, Edit, or Delete a specific resource (like a Document or a Performance). This was programmed within the ERB  partials which were rendered by the controller. However, in using AJAX/jQuery, this same content would no longer be rendered via ERB, and with jQuery’s inclusion in the asset pipeline, it is unable to directly access the model and application like ERB at runtime (at least for the purposes of this project, there are more advanced ways to “escape” JS and have it alternatively render ERB). Consequently, after thoroughly reviewing Pundit’s documentation, other programming websites, and lots of trial and error, I was able to determine I could create a method within the serializer for a model where it would call its Pundit policy, determine if the current user had access to read, edit, create, delete, etc., and then return an HTML string to the serializer to include the appropriate links within the “rights” key of the JSON. Then, once my webpage received the AJAX response, in addition to populating the data elements for an object instance (i.e. Document name, description, etc.), I could also insert the HTML returned for the CRUD actions.

class DocumentSerializer < ActiveModel::Serializer
  attributes :id, :contract_id, :name, :description, :location,  :rights
  def rights
    value =""
    if Pundit.policy(scope, Document.find(self.id)).show?
      value += "<a href=\"#{contract_document_path(self.contract_id, self.id)}\"><i class=\"fa fa-eye\"></i></a>  "
    end
    if Pundit.policy(scope, Document.find(self.id)).edit?
      value += "<a href=\"#{edit_contract_document_path(self.contract_id, self.id)}\" id=\"edit-doc\"><i class=\"fa fa-pencil\"></i></a>  "
    end
    if Pundit.policy(scope, Document.find(self.id)).destroy?
      value += "<a href=\"#{contract_document_path(self.contract_id, self.id)}\" id=\"del-doc\"><i class=\"fa fa-trash-o\"></i></a>  "
    end
    return value
  end
end

The second challenge I ran into was related to triggering the AJAX event on a page load. I was finding that when I navigated to a page that relied on an AJAX call  to populate data, the event would not fire and the data elements retrieved from that call would not get populated on the page unless I refreshed the page. This was due to interaction between Rails Turbolinks and jQuery. Turbolinks in and of itself, tries to help load pages faster by performing its own set of AJAX commands to render the DOM. However, this results in my JS listeners not being properly attached as Turbolinks would not load them. I utilized a gem called jquery-turbolinks to resolve these conflicts. However per the Rails Guides, you can also replace the $(document).ready event (to determine when the page has loaded) and attach the listeners, with $(document).on('page:load',function() {...}); in Rails 4 or $(document).on('turbolinks:load', function() {...}); in Rails 5.

Conclusion

JavaScript is an extremely powerful language that can greatly enhance the performance of a webpage and the user experience on the front-end of a web application. From the experience of working with jQuery on this project (and having used it on other web projects in the past), I’ve found it to be effective at piecemealing different functionalities together. But there are more powerful JS frameworks that allow you to better integrate the entire front-end and back-end together. AngularJS is one such framework and I’m excited to start learning about this prevalent library next.

About the author

Leave a Reply

Your email address will not be published. Required fields are marked *

About This Blog

This blog will serve as my outlet to share my thoughts and learnings as I dedicate myself to becoming a full-stack web developer...something too long in the making.

What I’ve Been Learning

+ Ruby
+ Ruby on Rails
+ HTML/CSS
+ JavaScript
+ jQuery
+ AngularJS

Archive