The overarching goal of this project was to take my existing Rails project (Subway Scheduler), and add dynamic features. To do this, we serialized the information in our ActiveRecord models, made them available via JSON API, and then used JavaScript to display that information, rather than utilizing only static Rails Views.
The requirements were further broken up as follows:
- Must translate JSON responses from your Rails app into JavaScript Model Objects using either ES6 class or constructor syntax. The Model Objects must have at least one method on the prototype.
- Must render at least one index page (
index
resource) via JavaScript and an Active Model Serialization JSON Backend. - Must render at least one show page (
show
resource) via JavaScript and an Active Model Serialization JSON Backend. - Your Rails application must dynamically render on the page at least one serialized
has_many
relationship through JSON using JavaScript. - Must use your Rails application to render a form for creating a resource that is submitted dynamically and displayed through JavaScript and JSON without a page refresh.
JavaScript Models: Addresses and Routes
I serialized both my Address and Route models using ActiveModel::Serializers
and made them available as JSON using Rails’ respond_to method.
As for the JavaScript models for each type of object, the constructors (and a formatter) looked like this:
Address
class Address { constructor(address) { this.id = address.id; this.line_1 = address.line_1; this.line_2 = address.line_2; this.city = address.city; this.zip = address.zip; this.path = `/addresses/${address.id}`; this.string = this.stringify(); } stringify() { var addressAsString = ""; addressAsString += `${this.line_1}, `; addressAsString += ((this.line_2) ? `${this.line_2}, ` : '' ); addressAsString += `${this.city}, NY ${this.zip}`; return addressAsString; } ... }
Route
class Route { constructor(route) { this.id = route.id; this.name = route.name; this.origin = new Address(route.origin); this.destination = new Address(route.destination); this.user = route.user; this.path = `/routes/${route.id}`; this.directions = []; } ... }
Rendering Address index page, adding new Addresses
First things first, I wanted to render my Address index page using JavaScript. First, had to make sure I could select where I wanted to put the addresses on the page by making sure the div
had an id
of 'address_list'
.
Then, I could call the Address
‘s JavaScript Model’s getAll
function once the page was ready:
class Address { ... static getAll(url) { allAddresses = []; $.get(`${url}.json`, function(response) { response.forEach(function(response_item) { var address = new Address(response_item); allAddresses.push(address); }); }).then(() => $("#address-list").html(HandlebarsTemplates['all-addresses']( {items: allAddresses}) ) ); return } ... }
That gets the addresses from the JSON API we created using our serializer, creates a new JavaScript Address
for each, and then adds each to the DOM using a Handlebars template.
Adding a new Address
The Address
index page also allows for adding new Addresses
, and rendering them dynamically. A “New Address” form with id 'new_address'
is watched for submissions. Upon being clicked, we serialize the form info, and POST
it to our API for storing in the database. Then, it’s added to the DOM by creating a new Address
JavaScript object and tacking it on the already-rendered list.
Showing a Route via our own API and Google Maps API
For showing a single Route
, we have to get origin and destination Address
information, plus use Google Maps’ Directions API to find actual routing information.
I wanted to actually load this information dynamically on a Route
list page, as I thought it would be more fun. So, on the Route
index page (or a User
‘s nested Route
index page), each Route has a “show route” button with class 'show_route'
, as well as a data-route-id
tag for tracking which button is clicked.
When that button is clicked, we search through our JavaScript Route
objects for the matching one, and call the showRoute
function on it:
$(document).on('click', '.show_route', function(e) { e.preventDefault(); const element = $(this) const route = allRoutes.find(function(rte) { return rte.id == element.data('route-id') }).showRoute(element) });
showRoute
gets the directions via Google Maps’ Directions API, and the object’s origin
and destination
Addresses
, and dynamically adds them to the DOM using another Handlebars template (FYI, directionsService
is a Google Maps DirectionsService
object):
class Route { ... showRoute(el) { const route = this; var request = { origin: route.origin.string, destination: route.destination.string, travelMode: 'TRANSIT' }; directionsService.route(request, function(result, status) { if (status == 'OK') { route.directions = result['routes'][0]['legs'][0]['steps'] el.after(HandlebarsTemplates['show-route'](route)); } }); } ... }