Last active
May 8, 2021 07:52
-
-
Save jaspertandy/e99c0d503f19510bc28a293d358ff274 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Controller } from "stimulus" | |
export default class extends Controller { | |
// this method is called when Stimulus attaches this | |
// class to an instance of a controller. It's like a | |
// constructor but I think it's when all the setup is done | |
// and we're ready to get started. That's how I use it anyway! | |
connect() { | |
this.updating = false // by default, we're not updating anything | |
this.timer = false // when updating on keypresses, we only do this on a timeout to reduce the number of requests sent | |
this.waiter = false // if a request is being sent at the same time as another, we need to wait but run anyway so we will check until this.updating is false again | |
this.lastSetQuantity = -1 // set the default to a value we never send, so that the first one is always successful | |
} | |
// this method is called directly by the blur event | |
updateQuantity(event) { | |
this.clearTimer() // any timers that didn't run yet can be cancelled in favour of this update | |
this.setCurrentQuantity() // try to set the current quantity | |
} | |
// this method is called directly by the keypress event | |
startTimer() { | |
this.clearTimer() // clear our old timers | |
this.timer = setTimeout(this.setCurrentQuantity.bind(this), 500) // try to set the current quantity, same as blur | |
} | |
// clear all running timers | |
// this may seem odd, but if we have waiters, we want the incoming timer to supersede | |
// them so they're safe to be trashed | |
clearTimer() { | |
clearInterval(this.timer) | |
clearInterval(this.waiter) | |
} | |
setCurrentQuantity() { | |
if (this.updating) { | |
this.waitThenRun() // if an update is already running, we spawn our waiter | |
return // then we're done here - this method will be re-entered when our waiter is done | |
} | |
// we aren't updating, but the value we're trying to set is the same | |
// as the last one, so running again would be redundant | |
if (this.lastSetQuantity == this.currentQuantity) { | |
return | |
} | |
this.updating = true // we're now updating | |
// build the data we're going to send to the server | |
const data = { | |
quantity: this.currentQuantity, | |
} | |
// I hate configuring URLs in JS. I always prefer to read them | |
// from data attributes controlled by my server, since the server | |
// is responsible for defining routes | |
fetch(this.endpoint, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify(data) | |
}).then(r => r.json()) | |
.then(this.doneUpdating.bind(this)) | |
} | |
// shorthand to get my URL for fetch | |
get endpoint() { | |
return this.quantityTarget.dataset.endpoint | |
} | |
// shorthand to read the current value of the field we're editing | |
get currentQuantity() { | |
return this.quantityTarget.value | |
} | |
// method to update my UI | |
doneUpdating(response) { | |
this.updating = false // we're done updating - let anyone who's waiting run | |
this.lastSetQuantity = response.data.quantity // the last successfully-set value | |
// any other UI updates here | |
} | |
waitThenRun() { | |
// there's already a timer running, we don't need another | |
if (this.waiter) { | |
return false | |
} | |
// wait for the update to be done, checking occasionally | |
this.waiter = setInterval(this.checkAndUpdate.bind(this), 1000) | |
} | |
checkAndUpdate() { | |
// updates we were waiting for are done | |
if (!this.updating) { | |
// we only clear the waiter here because the other timer might be | |
// waiting for typing to be finished and it's irrelevant to clear | |
// that here | |
clearInterval(this.waiter) | |
// and this is what we've been waiting for! | |
this.setCurrentQuantity() | |
} | |
} | |
// Stimulus stuff | |
static get targets() { | |
return [ | |
'quantity', | |
'total', | |
] | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment