ga-wdi-boston / full-stack-project

Other
8 stars 64 forks source link

jQuery hell #1049

Closed ryanwk closed 7 years ago

ryanwk commented 7 years ago

I'm using a data table. Buttons are added to each record on each successful crud action via subsequent event handlers (exerciseEvents.js & exerciseUi.js) It seems like jQuery is not attaching the event listener to my button when my datatable refreshes.

To clarify, my crud actions (update weight, remove exercise, add exercise, show all exercises) all work. However, after an exercise has been updated or removed from a single record within my datatable, and a user tries to use either the update or remove buttons of a different record, then things fall apart. The id for other records become undefined and I think jQuery is no longer attaching event listeners to the specified buttons that populate on each record within my datatable. However, sometimes you can add an exercise and things work normally again. Strange.

Due to this, I am not able to grab the id of a record utilizing this bit of jQuery:

  $('.updateWeightHandlebarsButton').on('click', (event) => {
    store.updating_id = event.target.dataset.id
    console.log(store.updating_id)
  })

My inability to grab the id of a record does not allow me to delete or update a record. The simple solution is and was to have a user input the id manually but I want this operation to be implicit. I also think, my trouble with jQuery here, is also why I can't hide my update and add exercise modals. Although, that might be an unrelated issue. For now the focus is on how to get my update and delete buttons working properly from within my datatable.

The datatable is created through this mechanism:

const showExerciseList = (data) => {
  const showExercisesHTML = showExercisesTemplate({ exercises: data.exercises })
  $('#library').show()
  $('#library tbody').empty()
  $('#library tbody').append(showExercisesHTML)
  $('#library').DataTable()
  $('.updateWeightHandlebarsButton').on('click', (event) => {
    store.updating_id = event.target.dataset.id
    console.log(store.updating_id)
  })

and then it is 'refreshed' by calling showExerciseList(data) from inside each successful event function (from within exerciseUi.js). Here's an example:

const addExerciseSuccess = (data) => {
  $('#content').text('You\'ve successfully added an exercise!')
  showExerciseList(data)
  console.log('addExercise success', data)
}

I feel like the crux of my problems stems from how I'm using jQuery.

I read up on dynamic content and jQuery, specifically delegated events: http://api.jquery.com/on/#direct-and-delegated-events Then read up on this issue on stackover flow. Tried the solution, no luck. https://stackoverflow.com/questions/15090942/event-handler-not-working-on-dynamic-content

I'm unsure of when and where I should place jQuery in order for my update and delete buttons to work properly. I'm preparing to methodically stick logs in everywhere and map the chain of events.

I need help organizing and structuring my event handling/success functions with jQuery, so that my delete and update buttons grab the id of a record within my data table, and enables my delete and update buttons to perform their respective operations.

events:

'use strict'
const exerciseUi = require('./exerciseUi')
const exerciseApi = require('./exerciseApi')
const getFormFields = require('../../../lib/get-form-fields')
const store = require('../store')

const onAddExerciseSubmit = (e) => {
  e.preventDefault()
  const data = getFormFields(event.target)
  exerciseApi.addExerciseRequest(data)
    .then(function () {
      exerciseUi.addExerciseSuccess
      onShowAllExercisesSubmit()
    })
    .catch(exerciseUi.addExerciseFail)
}

const onShowAllExercisesSubmit = () => {
  exerciseApi.showAllExercisesRequest()
    .then(exerciseUi.showAllExercisesSuccess)
    .then(() => $('.deleteButton').on('click', onRemoveExerciseClick))
    .catch(exerciseUi.showAllExercisesFail)
}

const onRemoveExerciseClick = (event) => {
  const id = $(event.target).attr('data-id')
  event.preventDefault()
  exerciseApi.removeExerciseRequest(id)
    .then(function () {
      exerciseUi.removeExerciseSuccess
      onShowAllExercisesSubmit()
    })
    .catch(exerciseUi.removeExerciseFailure)
}
const onUpdateWeightSubmit = (event) => {
  const id = store.updating_id
  console.log('weight updated', id)
  const userInput = getFormFields(event.target)
  event.preventDefault()
  exerciseApi.updateWeightRequest(userInput, id)
    .then(function () {
      exerciseUi.updateWeightSuccess
      onShowAllExercisesSubmit()
    })
    .catch(exerciseUi.updateWeightFailure)
}
// const onUpdateWeightCloseModal = () => {
//   $('#addExerciseModal').modal('hide')
//   console.log('closing add ex modal')
//   // event.preventDefault()
// }
const addExerciseModalEscape = () => {
  $('#inputNameAdd').val('')
  $('#inputWeightAdd').val('')
}

const updateWeightModalEscape = () => {
  $('#inputWeight').val('')
  $('#updateWeightID').val('')
}

const exerciseHandlers = function () {
  $('#addExerciseFormSubmit').on('submit', onAddExerciseSubmit)
  $('#showAllExercisesButton').on('click', onShowAllExercisesSubmit)
  $('#updateWeightFormSubmit').on('submit', onUpdateWeightSubmit)
  // $('#updateWeightFormSubmit').on('submit', onUpdateWeightCloseModal)
  $('.updateWeightClose').on('click', updateWeightModalEscape)
  $('.addExerciseClose').on('click', addExerciseModalEscape)
}

module.exports = {
  exerciseHandlers,
  onRemoveExerciseClick
}

ui:

'use strict'
const showExercisesTemplate = require('../templates/show-exercises.handlebars')
const store = require('../store')
const events = require('./exerciseEvents')

const showExerciseList = (data) => {
  const showExercisesHTML = showExercisesTemplate({ exercises: data.exercises })
  $('#library').show()
  $('#library tbody').empty()
  $('#library tbody').append(showExercisesHTML)
  $('#library').DataTable()
  $('.updateWeightHandlebarsButton').on('click', (event) => {
    store.updating_id = event.target.dataset.id
    console.log(store.updating_id)
  })
  // $('.deleteButton').on('click', events.onRemoveExerciseClick)
  $('.updateButton').on('submit', events.onUpdateWeightSubmit)
}

const addExerciseSuccess = (data) => {
  // $('#addExerciseModal').modal('hide')
  $('#content').text('You\'ve successfully added an exercise!')
  showExerciseList(data)
  console.log('addExercise success', data)
}

const addExerciseFail = () => {
  $('#directions').text('Something went wrong, please try again')
}

const showAllExercisesSuccess = (data) => {
  showExerciseList(data)
}

const showAllExercisesFail = () => {
  $('#directions').text('You don\'t have any exercises yet, click add exercises to create them!')
}
const removeExercisesSuccess = (data) => {
  $('#directions').text('Your exercise has been removed!')
  showExerciseList(data)
}

const removeExercisesFailure = () => {
  $('#directions').text('Something went wrong!')
}
const updateWeightSuccess = (data) => {
  $('#directions').text('Your exercise has been updated!')
  $('#updateWeightModal').modal('hide')
  showExerciseList(data)
}

const updateWeightFailure = () => {
  $('#directions').text('Something went wrong, please try again')
}

module.exports = {
  addExerciseSuccess,
  addExerciseFail,
  showAllExercisesSuccess,
  showAllExercisesFail,
  removeExercisesSuccess,
  removeExercisesFailure,
  updateWeightSuccess,
  updateWeightFailure
}

delete button (inside handlebars):

  <!-- remove exercise button -->
  <button data-id="{{exercise.id}}" type="button" class="deleteButton"><i class="fa fa-trash" aria-hidden="true"></i></button>

update weight button (inside handlebars):

  <!-- update exercise weight button -->
  <button data-id="{{exercise.id}}" type="button" id="updateWeightButton" class="btn btn-primary btn-sm updateWeightHandlebarsButton" data-toggle="modal" data-target="#updateWeightModal"><i class="fa fa-pencil" aria-hidden="true"></i> </button>

update exercise modal body (lives in index.html):

<!-- update exercise weight modal-->
<div class="forms">
  <div class="row">
    <div class="col-md-12">
      <!-- Modal contents -->
      <div class="modal fade" id="updateWeightModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
        <div class="modal-dialog" role="document">
          <div class="modal-content">
            <div class="modal-header">
              <button type="button" class="close updateWeightClose" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
              <h4 class="modal-title" id="myModalLabel">Update Weight</h4>
              <h5 id="updateWeightFailure"> </h5>
            </div>
            <div class="modal-body">
              <form id="updateWeightFormSubmit" class="updateButton form-horizontal">
                <div class="form-group">
                  <label for="inputWeight" class="col-sm-2 control-label">Exercise Weight</label>
                  <div class="col-sm-10">
                    <input name="exercise[weight]" type="number" class="form-control" id="inputWeight" placeholder="Exercise Weight (only accepts numbers)">
                  </div>
                  <!-- <div class="form-group">
                    <label for="inputName" class="col-sm-2 control-label">Exercise ID</label>
                    <div class="col-sm-10">
                      <input name="exercise[id]" type="text" class="form-control" id="updateWeightID" placeholder="Exercise ID">
                    </div>
                  </div> -->
                </div>
                <div class="form-group">
                  <div class="col-sm-offset-2 col-sm-10">
                    <button type="submit" class="btn btn-default">Update Weight</button>
                  </div>
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

datatable (lives in index.html)

<table id="library"
         class="table"
         data-toggle="table"
         data-sort-name="title"
         data-sort-order="desc">
      <thead>
      <tr>
          <th data-field="Exercise"
              data-sortable="true">
                  Exercise
          </th>
          <th data-field="Weight"
              data-sortable="true">
                  Weight
          </th>
          <th data-field="Date Performed"
              data-sortable="true">
                  Date Performed
          </th>
          <th data-field="ID"
              data-sortable="true">
                  ID
          </th>
          <th data-field="edit-delete"
    data-sortable="false">
        Edit/Delete
</th>
      </tr>
      </thead>
      <tbody>
      </tbody>
    </table>

Please help. The issue outlined above and getting my modals to close are the last things that I need to fix in order to resubmit.

@jordanallain @MicFin @tvlangley @payne-chris-r @sdavidson140 @Jcornmanhomonoff @bengitscode @benjimelito

ryanwk commented 7 years ago

I've pushed my code and deployed so that anyone can try it out and see what I'm experiencing

cpearce31 commented 7 years ago

Take a closer look here:

 exerciseApi.updateWeightRequest(userInput, id)
    .then(function () {
      exerciseUi.updateWeightSuccess
      onShowAllExercisesSubmit()
    })
    .catch(exerciseUi.updateWeightFailure)

The line exerciseUi.updateWeightSuccess is a reference to a function, not a function call. That would be correct if you were just passing it into a .then(), but you're not, you're creating an anonymous function inside the .then(). That means that exerciseUi.updateWeightSuccess is never firing.

cpearce31 commented 7 years ago

I think there's a few other problems that are making this more difficult to debug, as well. You reference exerciseUi.removeExerciseSuccess but you don't have a function with that name. Also, be careful about relying on event.target for click events, it's not always what you might think it is. Try logging it.

payne-chris-r commented 7 years ago

1) you're in to something with the idea of adding logs in the chain so you can see it yourself 2) Caleb is absolutely right 3) the best way to do this is with subsequent .then()s instead of putting both functions in the same block

ryanwk commented 7 years ago

fixed this by following Caleb's recommendations:

.then(exerciseUi.updateWeightSuccess)

and adding this to the icon tags within my update button in handlebars:

data-id="{{exercise.id}}"