Trail ValetV2: A Day at the SPA

Zachary Williams
4 min readJun 1, 2021

--

In the fourth phase in coding bootcamp I was tasked with building a single page application(SPA) in JavaScript(JS). I felt this was a good opportunity to refactor my previous project since it was essentially a todo list of trail issues that needed to be created and checked off. JS is code that is always running and gives a user a seamless experience if done correctly.

I started my project my planning out my models and how they would interact. I decided to have a trail model that had many valets. Valets would be the issues a user could create that needed to be fixed. The trail model would have a name and the valet model would have the location, issue, and whether or not it was fixed. I then initialized my Rails backend using the API flag. This leaves out files that won’t be needed on the front end such as views and helpers. I then used scaffolding to build out my models. This generated the majority of my backend that I would be manipulating with my frontend using JS.

JavaScript(JS) was developed in the mid 90s in a week. It was largely forgotten until about 10 years later when sites like Google Maps and Facebook used it to make their applications on the web more dynamic.

getTrails(){fetch(this.baseTrailURL).then(r => r.json()).then(trails => {trails.forEach(trail => {const t = new Trail(trail)t.buildTrailHtml()t.valets.forEach(valet => {const v = new Valet(valet)v.buildValetHtml()})})}).catch(error => console.error(error))}

The code above is what I used to pull my data from my API. Fetch goes to my rails API and retrieves JSON(Javascript Object Notation) data using a GET request. It then takes that data to build objects using my JS trail and valet classes then turns the objects into HTML that is displayed on the users page. To make my page more dynamic I created a form for users to add issues to trail that needed to be fixed.

addValetToTrail(){const trail = document.getElementById(`create-valet-button-for-trail-${this.id}`)const valetForm = document.createElement('form')valetForm.id = `Add-valet-form-to-trail-${this.id}`valetForm.trailID = this.idvaletForm.innerHTML = `<input id='form-valet-location' placeholder='location' type='text'/><br><input id='form-valet-issue' placeholder='issue' type='text'/><br><input value='Create Valet for ${this.name}' type='submit'>`trail.append(valetForm)valetForm.addEventListener(`submit`, this.createValet)}

The code above creates the form to add an issue to a trail. At the bottom is an event listener. This “listens” for an action by a user. In this case it is listening for the ‘submit’ button to be clicked and then a callback function follows. A callback function is one that will be called after the action that precedes it is executed. Below is the code for the callback function. It uses the target from the the event to located the data fields. Then we use fetch and a POST method to send the data to backend.

event.preventDefault()const valetLocation = event.target.children[0]const valetIssue = event.target.children[2]const trailID = this.trailIDconsole.log(trailID)fetch("http://localhost:3000/valets", {method: "POST",headers: {"Content-Type": "application/json",Accept: "application/json"},body: JSON.stringify({location: valetLocation.value,issue: valetIssue.value,trail_id: trailID})}).then(resp => resp.json()).then(valetData => {if (valetData.status === "found"){const nt = new Valet(valetData.valet)nt.buildValetHtml()valetIssue.value = ""valetLocation.value = ""}else {alert(valetData.errors)}}).catch(err => console.error(err))}

If the server responds with “found” it will use the updated information from the server build new HTML to add to the list under the appropriate trail using the trail id.

The next thing I wanted to do was allow a user to “check off” completed valet issues. This was a similar approach to the form. I used an event listener and fetch except this time it was PATCH. This is part of RESTful convention which you can find out more about here:

.then(data  => {if (data.status === 200) {const valetItem = document.getElementById(`valet-id-${data.valet.id}`)valetItem.children[2].innerText = "fixed"valetItem.children[3].hidden = data.valet.fixed

If the server updates the issue it then finds the item on the page using the valet id, changes the issue to fixed, and hides the button.

Up to this point the page is a minimum viable product(MVP). In the future I have plans to expand on the trail models by giving them parent locations and give the valet objects more attributes to allow users to exchange information about how to best approach solving the issues and voting on how to fix the issues.

--

--