ga-wdi-boston / capstone-project

Other
3 stars 28 forks source link

Update place notes attribute #695

Closed eliottenos closed 7 years ago

eliottenos commented 7 years ago

Im trying to update the notes attribute using handlebars but the page just refreshes on submit, not hitting the server. Im trying to use the same slice method to get the data id and then getFormField to get the updated not

{{#each locations as |location|}}
<h3>
  Location:
</h3>
<h4>
 {{ location.country }}
</h4>
<h4>
  {{ location.note }}
</h4>
<button id="delete-{{ location.id }}" type="button" class="delete-location">Delete Location</button>
<p>Update Location Notes:</p>
<form class='locations-update' id='update-{{ location.id }}'>
 <div class="form-group">
   <input type="text" name="place[note]"placeholder="Note">
 <input type="submit" name="submit" value="Submit">
</form>
{{/each}}
const onUpdateLocation = function (event) {
  event.preventDefault()
  // const place = event.target
  const data = getFormFields(event.target)
  const index = event.target.id.indexOf('-')
  const updateLocation = event.target.id.slice(index + 1)
  console.log('id here', updateLocation)
  api.updateLocation(updateLocation, data)
    .then(ui.updateLocationSuccess)
    .catch(ui.updateLocationFailure)
}

const addHandlers = () => {
  $('.content-div2').on('submit', '#location-create', onCreateLocation)
  $('.content-div2').on('submit', '.location-update', onUpdateLocation)
  // $('.content-div2').on('click', '#location-country-delete', onDeleteLocation)
  $('.content-div2').on('click', '.delete-location', onDeleteLocation)
  $('.content-div2').on('click', '#location-get', onGetAllLocations)
}
const updateLocation = (data) => {
  return $.ajax({
    url: config.apiOrigin + '/update-place/' + data.place.id,
    method: 'PATCH',
    headers: {
      Authorization: 'Token token=' + store.user.token
    },
    data: data
  })
}

Any suggestions?

MicFin commented 7 years ago

Share your index.html file Share your index.js file Share the file that addHandlers is defined in

eliottenos commented 7 years ago

@MicFin index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Map</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Do not add `link` tags unless you know what you are doing -->
    <link rel="shortcut icon" type="image/x-icon" href="favicon.ico">

    <!-- Do not add `script` tags unless you know what you are doing -->
    <script src="public/vendor.js" type="text/javascript" charset="utf-8" defer></script>
    <script src="public/application.js" type="text/javascript" charset="utf-8" defer></script>
  </head>
  <body class="container-fluid">
    <nav class="navbar navbar-default navbar-div" role="navigation"></nav>
    <div class="container wrapper-div">
      <div class="content-div"></div>
      <!-- <div class="content-div2"></div> -->
      <!-- temporary forms for front end testing -->
      <div class="temp-div"></div>
    </div>
    <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.6.9/topojson.min.js"></script>
<script src="node_modules/datamaps/dist/datamaps.world.min.js"></script>
<div class="map" id="container" style="position: relative; width: 1000px; height: 600px;"></div>
<!-- <script>
    var map = new Datamap({element: document.getElementById('container')});
</script> -->
    <script type="text/javascript">
</script>
<script>
    const map = new Datamap({
        element: document.getElementById('container'),
        done: function(datamap) {
            datamap.svg.selectAll('.datamaps-subunit').on('click', function(geography) {
                alert(geography.properties.name);
            })
        }
    })
</script>
<div class="content-div2"></div>
  </body>
</html>

index.js

const setAPIOrigin = require('../../lib/set-api-origin')
const config = require('./config')
const view = require('./view')
const authEvents = require('./auth/events')
const locationEvents = require('./locations/events')
// const CSSMap = require('cssmap-europe/cssmap-europe.css')

$(() => {
  setAPIOrigin(location, config)
  view.initView()
  authEvents.addHandlers()
  locationEvents.addHandlers()
}
)

// use require with a reference to bundle the file and use it in this file
// const example = require('./example')

// use require without a reference to ensure a file is bundled
require('./example')

addHandlers is in view.js

// const store = require('./store')

// initializes view containers and event handlers

const initView = () => {
  // render private view to navbar-div
  renderView('.navbar-div', 'nav-public')
  // console.log('hit render view')
  // user sign up / sign in forms
  renderView('.content-div', 'form-auth')
  $('.map').hide()
  // add event handlers for view contoller elements
  addHandlers()
}

//
// VIEW RENDERING FUNCTIONS
//

// renderView(element, hbsFile, params)
// renders the template and replaces the element

const renderView = (element, hbsFile, params) => {
  const template = require(`./templates/${hbsFile}.handlebars`)
  const content = template(params)
  $(element).html(content)
}

// replaceView(element, hbsFile, params)
// renders the template and replaces the element

const replaceView = (element, hbsFile, params) => {
  const template = require(`./templates/${hbsFile}.handlebars`)
  const content = template(params)
  $(element).replaceWith(content)
}

// appendView(element, filepath, params)
// renders the template and appends it to the element

const appendView = (element, hbsFile, params) => {
  const template = require(`./templates/${hbsFile}.handlebars`)
  const content = template(params)
  $(element).append(content)
}

// prependView(element, filepath, params)
// renders the template and appends it to the element

const prependView = (element, hbsFile, params) => {
  const template = require(`./templates/${hbsFile}.handlebars`)
  const content = template(params)
  $(element).prepend(content)
}

// clearView(element)
// clears the html from the specified element

const clearView = (element) => {
  $(element).html('')
}

//
// PUBLIC AND PRIVATE MODES
//

// setPublicMode()
// set public mode for navbar and content area

const setPublicMode = () => {
  // closeAlert()
  renderView('.navbar-div', 'nav-public')
  renderView('.content-div', 'form-auth')
  clearView('.temp-div')
}

const setPrivateMode = () => {
  // closeAlert()
  renderView('.navbar-div', 'nav-private')
  // renderView('.content-div', 'map')
  renderView('.content-div2', 'locations')
  $('.map').show()
  // $('.form-auth').hide()
}

//
// VALIDATION & ALERT METHODS
//

// formAlert(form, field)
// triggers form input validation alert

const formAlert = (form, field) => {
  clearFormAlerts(form)
  // apply alert classes to specfic input
  $(field).closest('.form-group').addClass('has-warning has-feedback')
  // add alert icon to specific input
  $(field).closest('.input-group').find('.form-control').after(`<span class="glyphicon glyphicon-warning-sign form-control-feedback"></span>`)
  // show help text for specific input
  $(field).closest('.form-group').find('.help-block').show()
}

// clearFormFields(form)
// clear all values from form fields

const clearForm = (form) => {
  // clear form field alerts
  clearFormAlerts(form)
  // clear field values
  $(form).find('.form-control').val('')
}

// clearFormAlerts(form)
// clear all feedback classes and icons from form fields

const clearFormAlerts = (form) => {
  // clear all alert classes from inputs
  $(form).find('.form-group').removeClass('has-warning has-feedback')
  // remove all alert class icons from inputs
  $(form).find('.form-group .form-control-feedback').remove()
  // hide any visible help text
  $(form).find('.help-block').hide()
}

// showAlert(mode, message)
// displays global alert box for info or warning

const showAlert = (mode, message) => {
  // convert mode label to bootstrap class
  mode = (mode === 'error') ? 'danger' : 'info'
  // if there's already an alert
  if ($('.alert').length) {
    // replace the existing alert
    replaceView('.alert', 'alert', { mode: mode, message: message })
  } else {
    // insert a new alert
    prependView('.content-div', 'alert', { mode: mode, message: message })
  }
}

// closeError()
// close global error box but not info alerts

const closeError = () => {
  $('.alert-danger').alert('close')
}

// closeAlert()
//  close all global alert boxes

const closeAlert = () => {
  $('.alert').alert('close')
}

// showChangePasswordSuccess()
// password changed successfully

const showChangePasswordSuccess = () => {
  // collapse change password dropdown
  $('#change-password-nav').dropdown('toggle')
  $('.navbar-collapse').collapse('hide')
  // clear change password form fields
  $('#change-password input').val('')
  // display successful alert message
  showAlert('info', 'Your password is changed. Hope you remember it.')
}

// showChangePasswordFailure()
// password change failed

const showChangePasswordFailure = () => {
  // collapse change password dropdown
  $('#change-password-nav').dropdown('toggle')
  $('.navbar-collapse').collapse('hide')
  // clear change password form fields
  $('#change-password input').val('')
  // display successful alert message
  showAlert(`error`, `For highly complex reasons, your password couldn't be changed.`)
}

const addHandlers = () => {
  // DROPDOWN MENU EVENTS
  // add animation to dropdown expand
  $('.navbar-div').on('show.bs.dropdown', '.dropdown', (event) => {
    $(event.target).find('.dropdown-menu').first().stop(true, true).slideDown(250)
  })

  // add animation to dropdown collapse
  $('.navbar-div').on('hide.bs.dropdown', '.dropdown', (event) => {
    event.preventDefault()
    $(event.target).find('.dropdown-menu').first().stop(true, true).slideUp(
      250, () => {
        // close dropdown menu
        $('.dropdown').removeClass('open')
        $('.dropdown').find('.dropdown-toggle').attr('aria-expanded', 'false')
        // clear fields
        clearForm($(event.target).find('.form').val('id'))
      })
  })
}

module.exports = {
  initView,
  setPublicMode,
  setPrivateMode,
  showAlert,
  formAlert,
  clearForm,
  clearFormAlerts,
  closeError,
  closeAlert,
  showChangePasswordSuccess,
  showChangePasswordFailure,
  appendView
}
MicFin commented 7 years ago

Is there a reason why <!-- <div class="content-div2"></div> --> is commented out? Your click handler is applied to an element with class content-div2

$('.content-div2').on('submit', '.location-update', onUpdateLocation)
eliottenos commented 7 years ago

yep, ive used it somewhere else but cant find it right now. If I uncomment it renders twice

eliottenos commented 7 years ago

ahhhh, its at the bottom of index.html

MicFin commented 7 years ago

Where is the element with the class location-update?

$('.content-div2').on('submit', '.location-update', onUpdateLocation)
eliottenos commented 7 years ago

in get-locations.handlebar

{{#each locations as |location|}}
<h3>
  Location:
</h3>
<h4>
 {{ location.country }}
</h4>
<h4>
  {{ location.note }}
</h4>
<button id="delete-{{ location.id }}" type="button" class="delete-location">Delete Location</button>
<p>Update Location Notes:</p>
<form class='location-update' id='update-{{ location.id }}'>
 <div class="form-group">
   <input type="text" name="place[note]"placeholder="Note">
 <input type="submit" name="submit" value="Submit">
</form>
{{/each}}
eliottenos commented 7 years ago

I feel like its a problem with my function in events.js or how im sending the ajax call? @MicFin

MicFin commented 7 years ago

Why are there 2 addHandlers functions?

The addHandlers function that is called in the index.js is from the view.js:

// addHandlers is in view.js
const addHandlers = () => {
  // DROPDOWN MENU EVENTS
  // add animation to dropdown expand
  $('.navbar-div').on('show.bs.dropdown', '.dropdown', (event) => {
    $(event.target).find('.dropdown-menu').first().stop(true, true).slideDown(250)
  })

  // add animation to dropdown collapse
  $('.navbar-div').on('hide.bs.dropdown', '.dropdown', (event) => {
    event.preventDefault()
    $(event.target).find('.dropdown-menu').first().stop(true, true).slideUp(
      250, () => {
        // close dropdown menu
        $('.dropdown').removeClass('open')
        $('.dropdown').find('.dropdown-toggle').attr('aria-expanded', 'false')
        // clear fields
        clearForm($(event.target).find('.form').val('id'))
      })
  })
}

But there is another function addHandlers that actually has the form event we want, what file is that in?

const addHandlers = () => {
  $('.content-div2').on('submit', '#location-create', onCreateLocation)
  $('.content-div2').on('submit', '.location-update', onUpdateLocation)
  // $('.content-div2').on('click', '#location-country-delete', onDeleteLocation)
  $('.content-div2').on('click', '.delete-location', onDeleteLocation)
  $('.content-div2').on('click', '#location-get', onGetAllLocations)
}
eliottenos commented 7 years ago

thats in locations/events.js its required in index.js

const locationEvents = require('./locations/events')

and called with

locationEvents.addHandlers()
MicFin commented 7 years ago

Can you share all of ./locations/events.js?

eliottenos commented 7 years ago
const getFormFields = require(`../../../lib/get-form-fields`)
// const locationListing = require('../templates/locations.handlebars')

const api = require('./api')
const ui = require('./ui')

const onCreateLocation = function (event) {
  const data = getFormFields(event.target)
  event.preventDefault()
  api.createLocation(data)
    .then(ui.createLocationSuccess)
    .catch(ui.createLocationFailure)
}

const onGetAllLocations = function () {
  api.getLocation('')
      .then(ui.getLocationSuccess)
      .catch(ui.getLocationError)
  // } else {
  //   console.log('Please provide a location id!')
}

// const onUpdateLocation = function (event) {
//   event.preventDefault()
//   // console.log('inside of onUpdateLocation')
//   const data = getFormFields(event.target)
//   api.updateLocation(data)
//   .then(ui.updateLocationSuccess)
//   .catch(ui.updateLocationFailure)
//   // }
// }

const onUpdateLocation = function (event) {
  event.preventDefault()
  // const place = event.target
  const data = getFormFields(event.target)
  const index = event.target.id.indexOf('-')
  const updateLocation = event.target.id.slice(index + 1)
  console.log('id here', updateLocation)
  api.updateLocation(updateLocation, data)
    .then(ui.updateLocationSuccess)
    .catch(ui.updateLocationFailure)
  // } else {
  //   console.log('Please provide a location id!')
}

const onDeleteLocation = function (event) {
  // event.preventDefault()
  // const place = event.target
  const index = event.target.id.indexOf('-')
  const deleteLocation = event.target.id.slice(index + 1)
  console.log('id here', deleteLocation)
  api.deleteLocation(deleteLocation)
    .then(ui.deleteLocationSuccess)
    .catch(ui.deleteLocationFailure)
  // } else {
  //   console.log('Please provide a location id!')
}

const addHandlers = () => {
  $('.content-div2').on('submit', '#location-create', onCreateLocation)
  $('.content-div2').on('submit', '.location-update', onUpdateLocation)
  // $('.content-div2').on('click', '#location-country-delete', onDeleteLocation)
  $('.content-div2').on('click', '.delete-location', onDeleteLocation)
  $('.content-div2').on('click', '#location-get', onGetAllLocations)
}

module.exports = {
  addHandlers
}
MicFin commented 7 years ago

Add the following alerts and let me know which ones pop up. We are using alert instead of console.log because your page is reloading so it may reload before we can check what is logged but each alert will be seen immediately when triggered.

// index.js
$(() => {
  setAPIOrigin(location, config)
  view.initView()
  authEvents.addHandlers()
  alert('index.js file')
  locationEvents.addHandlers()
})
// location/events.js
const addHandlers = () => {
  $('.content-div2').on('submit', '#location-create', onCreateLocation)
  alert("events.js file")
  $('.content-div2').on('submit', '.location-update', onUpdateLocation)
  // $('.content-div2').on('click', '#location-country-delete', onDeleteLocation)
  $('.content-div2').on('click', '.delete-location', onDeleteLocation)
  $('.content-div2').on('click', '#location-get', onGetAllLocations)
}
const onUpdateLocation = function (event) {
  event.preventDefault()
  alert("onUpdateLocation")
  const data = getFormFields(event.target)
  const index = event.target.id.indexOf('-')
  const updateLocation = event.target.id.slice(index + 1)
  console.log('id here', updateLocation)
  api.updateLocation(updateLocation, data)
    .then(ui.updateLocationSuccess)
    .catch(ui.updateLocationFailure)
  // } else {
  //   console.log('Please provide a location id!')
}
eliottenos commented 7 years ago

it hits index.js and events.js but not onUpdateLocation

MicFin commented 7 years ago

The HTML in this Handlebars template has a <div> that needs to be closed before the <form> closes.

<form class='location-update' id='update-{{ location.id }}'>
 <div class="form-group">
   <input type="text" name="place[note]"placeholder="Note">
   <input type="submit" name="submit" value="Submit">
</form>
eliottenos commented 7 years ago

oh yeah, still getting the same response, not hitting onUpdateLocation

MicFin commented 7 years ago

Delegated events have to be on a parent element for a child element's event.

$('.content-div2').on('submit', '.location-update', onUpdateLocation)

The element with the class content-div2 has to be a parent element of your form with class location-update. Is that the case?

eliottenos commented 7 years ago
<div class="content-div2"></div>

is just an empty div in index.html where to render content

MicFin commented 7 years ago

Does it ever get removed or replaced in the DOM? Let's try changing $('.content-div2').on('submit', '.location-update', onUpdateLocation) to:

$('body').on('submit', '.location-update', onUpdateLocation)
eliottenos commented 7 years ago

its used in view.js is all I can think?

const setPrivateMode = () => {
  // closeAlert()
  renderView('.navbar-div', 'nav-private')
  // renderView('.content-div', 'map')
  renderView('.content-div2', 'locations')
  $('.map').show()
  clearView('.content-div', 'form-auth')
  // $('.form-auth').hide()
}

is that applicable?

MicFin commented 7 years ago

What results did this give you?

$('body').on('submit', '.location-update', onUpdateLocation)
eliottenos commented 7 years ago

the same result, only hitting index.js and events.js alerts I've pushed what I have to master branch if thats easier to look at https://github.com/eliottenos/capstone-client

MicFin commented 7 years ago

Last bit of debugging I can do for the night.
Check the value of the callback you are passing. Check the jQuery selector you are using.

const addHandlers = () => {
  $('.content-div2').on('submit', '#location-create', onCreateLocation)
  console.log("onupdate is ", onUpdateLocation)
  console.log("content div is ", $('.content-div2'))

  // comment out the event so the page doesn't reload on submit
  // $('.content-div2').on('submit', '.location-update', onUpdateLocation)
  // $('.content-div2').on('click', '#location-country-delete', onDeleteLocation)
  $('.content-div2').on('click', '.delete-location', onDeleteLocation)
  $('.content-div2').on('click', '#location-get', onGetAllLocations)
}
eliottenos commented 7 years ago

screen shot 2017-06-16 at 00 41 50

MicFin commented 7 years ago

Previously I said:

Delegated events have to be on a parent element for a child element's event. $('.content-div2').on('submit', '.location-update', onUpdateLocation) The element with the class content-div2 has to be a parent element of your form with class location-update. Is that the case?

It looks like you are adding the template with the form here:

const getLocationSuccess = (data) => {
  $('.createSuccess').empty()
  // console.log('this one', data)
  // console.log($(data))
  let locations = showLocationTemplate({ locations: data.places })
  $('.getBlank1').append(locations)
}

And appending it to the element with getBlank1 that is in the locations.handlebars template but when is the locations.handlebars template added to the DOM? It looks commented out in locations/ui.js and not added to the DOM.

MicFin commented 7 years ago

Additionally, you have a couple other javascript files from a library mixed in there, if they are adding events to the form that would also be problematic. Don't stay up all night on this.

eliottenos commented 7 years ago

great, will look into it, thanks Mike