Survey App for Flatiron Project

Introduction

Requirements

This was an exciting project as it pulled together all that I have been learning at Flatiron School over the last four months. I have come to appreciate the simplicity of setting up an API with Rails. The project requirements were as follows:

App Design

HTML - the footprint starts with a HTML file loosely based on HTML5 Boilerplate project, with a few of my own modification. I prefer to group the folder structure to separate concerns so, the source files are grouped into a src which includes separate folders for js, styling, and images. The compiled and minified files for production, are grouped into a 'dist' folder structure, again separated by js, styling, and images.

Styling - Most projects I have spun put pretty quickly and have relied on Component UI's to decrease the development time. I have used Bootstrap, and TailwindCSS in the past. This site is built with Bulma, which I love.

API - the API is changed with Ruby on Rails in API Mode utilizing a Postgres database. There are two database tables: 1) Surveys to save each survey list and three questions, and 2) an Answers table which saves the survey responses and the corresponding survey_id.

Fetch API

To set up the index page when I user visits the site, I used a simple GET request using the Fetch API. It is with this design I encountered a bug and an opportunity for learning. The following fetch call was at the head of the index.js file.

  fetch('http://localhost:3000/surveys')
.then(res => res.json())
.then(surveys => {
surveys.forEach(survey => {
const { id, title, question1, question2, question3 } = survey
new Survey(id, title, question1, question2, question3)
})
})

When the user visited a single Survey page and clicked delete, the survey was in fact deleted, but it required a manual refresh to bring back the index display. I refactored the root fetch call:

function fetchSurveys() {
fetch('http://localhost:3000/surveys')
.then(res => res.json())
.then(surveys => {
surveys.forEach(survey => {
const { id, title, question1, question2, question3 } = survey
new Survey(id, title, question1, question2, question3)
})
})
}
fetchSurveys()

This refactor meant in the deleteSurvey method in the Survey class I could call this function to display the Surveys again:

async deleteSurvey() {
await fetch(`http://localhost:3000/surveys/${ this.id }`, {
method: 'DELETE'
})
.then(() => {
document.getElementById('survey-container')
.removeChild(document.getElementById(this.id))
})
fetchSurveys()
}

Promise Pretty Please?

The next lesson I learned in this project was that a Promise is NOT the same as DATA. I struggled when I realized I could not really create a "global variable" to use throughout the project. I ended up utilizing JavaScript to Manipulate the Document Object Model to interject the results of the survey. I would love to abstract this code but it works:

getResults() {
const fetchPromise = fetch('http://localhost:3000/answers')
const resultsReport1 = document.getElementById('q1')
const resultsReport2 = document.getElementById('q2')
const resultsReport3 = document.getElementById('q3')
fetchPromise.then(resp => {
return resp.json()
}).then(questionResults => {
const myResults1 = questionResults.filter(a => a.surveys_id && a.responded === 'question1').length
resultsReport1.innerHTML += myResults1
const myResults2 = questionResults.filter(a => a.surveys_id && a.responded === 'question2').length
resultsReport2.innerHTML += myResults2
const myResults3 = questionResults.filter(a => a.surveys_id && a.responded === 'question3').length
resultsReport3.innerHTML += myResults3
})
}

Which manipulates the DOM based on this template:

resultsHTML() {
return `
<div id="results-card">
<h3>Results:</h3>
<ul class="report-list">
<li>
${ this.question1 }: <span id="q1"></span></li>
<li>
${ this.question2 }: <span id="q2"></span></li>
<li>
${ this.question3 }: <span id="q3"></span></li>
</ul>
</div>
<button class="card__btn done">Done</button>
`

}

Overall, this has been a great learning experience of a Single Page Application and there is plenty of room for future upgrades. Are you interested? Go look at the repo for future features.