DEV Community

Cover image for Survey App Project for Flatiron
Chuck
Chuck

Posted on

Survey App Project for Flatiron

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:

  • Build a Single Page Application (SPA), with the frontend built with HTML, CSS, and JavaScript and a backend API with Ruby and Rails.
  • Communication between the front and backend using asynchronously (AJAX) communication and JSON as the communication format.
  • The JavaScript application must use Object Oriented JavaScript (classes) to encapsulate related data and behavior.
  • The domain model served by the Rails backend must include a resource with at least one has-many relationship.
  • The backend and frontend must collaborate to demonstrate Client-Server Communication. Your application should have at least 3 AJAX calls, covering at least 2 of Create, Read, Update, and Delete (CRUD). Your client-side JavaScript code must use fetch with the appropriate HTTP verb, and your Rails API should use RESTful conventions.

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.

  • SCSS Boilerplate I customized based on original work by Hugo Giraudel and his SASS_Boilerplate
  • Styling is formatted, compiled, and minified using Gulp, and Browersync. The Gulp file is my tweaks to a Gulp-Boilerplate originally designed by Chris Ferdinandi
  • The App allows users to create, delete, and complete (update) surveys, which then will display a running result (this is not the best design, as a admin account needs to added to handle delete, but this meets the requirements of the project). It has been very satisfy to code the styling for this project.

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)
      })
    })
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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()
  }
Enter fullscreen mode Exit fullscreen mode

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
    })
  }
Enter fullscreen mode Exit fullscreen mode

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>
    `
  }
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (3)

Collapse
 
speckworks profile image
John K. Speck

Awesome work, Chuck! I think you're going to love the ReactJS framework if you go that direction. It Really helps make the DOM manipulation stuff a bit more elegant.

Collapse
 
eclecticcoding profile image
Chuck • Edited

Thanks John.
My story: I started learning Gatsby and React last February with no JS experience. HARD! This inspired me, after years of WordPress experience, to decide to level up a 20 year hobby for coding and tech into a future career. I graduate from Flatiron in a month and start the job search with experience in Ruby, Sinatra, Rails, JavaScript and React. I have learned Nuxt as well in an attempt to level up that tool set. I see pluses and minuses in both frameworks and love learning. Thanks for your interest.

Collapse
 
speckworks profile image
John K. Speck

That's inspiring, Chuck! I finished Flatiron January 3rd here in Brooklyn. It was a life-changing journey. My final project used a Rails Backend and the ReactJS framework for the frontend(actually pretty straightforward if someone helps you get "conditional rendering"). I was also able to add some data visualization using the Chart.js library and an interactive map component with Google React Maps component. I am happy to report that I spoke with a Real Live recruiter today and job prospects are in my near future. Good Luck with everything, Chuck! Flatiron is a great program. Feel free to keep in touch.