app/javascript/blacklight/checkbox_submit.js in blacklight-7.40.0 vs app/javascript/blacklight/checkbox_submit.js in blacklight-8.0.0.beta1

- old
+ new

@@ -1,135 +1,80 @@ -/* A JQuery plugin (should this be implemented as a widget instead? not sure) - that will convert a "toggle" form, with single submit button to add/remove +/* Converts a "toggle" form, with single submit button to add/remove something, like used for Bookmarks, into an AJAXy checkbox instead. - Apply to a form. Does require certain assumption about the form: 1) The same form 'action' href must be used for both ADD and REMOVE actions, with the different being the hidden input name="_method" being set to "put" or "delete" -- that's the Rails method to pretend to be doing a certain HTTP verb. So same URL, PUT to add, DELETE to remove. This plugin assumes that. - Plus, the form this is applied to should provide a data-doc-id attribute (HTML5-style doc-*) that contains the id/primary key of the object in question -- used by plugin for a unique value for DOM id's. - - Uses HTML for a checkbox compatible with Bootstrap 3. - - Pass in options for your class name and labels: - $("form.something").blCheckboxSubmit({ - //cssClass is added to elements added, plus used for id base - cssClass: "toggle_my_kinda_form", - error: function() { - #optional callback - }, - success: function(after_success_check_state) { - #optional callback - } - }); + Uses HTML for a checkbox compatible with Bootstrap 4. + new CheckboxSubmit(document.querySelector('form.something')).render() */ -(function($) { - $.fn.blCheckboxSubmit = function(argOpts) { - this.each(function() { - var options = $.extend({}, $.fn.blCheckboxSubmit.defaults, argOpts); +export default class CheckboxSubmit { + constructor(form) { + this.form = form + } + async clicked(evt) { + this.spanTarget.innerHTML = this.form.getAttribute('data-inprogress') + this.labelTarget.setAttribute('disabled', 'disabled'); + this.checkboxTarget.setAttribute('disabled', 'disabled'); + const response = await fetch(this.formTarget.getAttribute('action'), { + body: new FormData(this.formTarget), + method: this.formTarget.getAttribute('method').toUpperCase(), + headers: { + 'Accept': 'application/json', + 'X-Requested-With': 'XMLHttpRequest', + 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]')?.content + } + }) + this.labelTarget.removeAttribute('disabled') + this.checkboxTarget.removeAttribute('disabled') + if (response.ok) { + const json = await response.json() + this.updateStateFor(!this.checked) + document.querySelector('[data-role=bookmark-counter]').innerHTML = json.bookmarks.count + } else { + alert('Error') + } + } - var form = $(this); - form.children().hide(); - //We're going to use the existing form to actually send our add/removes - //This works conveneintly because the exact same action href is used - //for both bookmarks/$doc_id. But let's take out the irrelevant parts - //of the form to avoid any future confusion. - form.find('input[type=submit]').remove(); + get checked() { + return (this.form.querySelectorAll('input[name=_method][value=delete]').length != 0) + } - //View needs to set data-doc-id so we know a unique value - //for making DOM id - var uniqueId = form.attr('data-doc-id') || Math.random(); - // if form is currently using method delete to change state, - // then checkbox is currently checked - var checked = (form.find('input[name=_method][value=delete]').length != 0); + get formTarget() { + return this.form + } - var checkbox = $('<input type="checkbox">') - .addClass( options.cssClass ) - .attr('id', options.cssClass + '_' + uniqueId); - var label = $('<label>') - .addClass( options.cssClass ) - .attr('for', options.cssClass + '_' + uniqueId) - .attr('title', form.attr('title') || ''); - var span = $('<span>'); + get labelTarget() { + return this.form.querySelector('[data-checkboxsubmit-target="label"]') + } - label.append(checkbox); - label.append(' '); - label.append(span); + get checkboxTarget() { + return this.form.querySelector('[data-checkboxsubmit-target="checkbox"]') + } - var checkboxDiv = $('<div class="checkbox" />') - .addClass(options.cssClass) - .append(label); + get spanTarget() { + return this.form.querySelector('[data-checkboxsubmit-target="span"]') + } - function updateStateFor(state) { - checkbox.prop('checked', state); - label.toggleClass('checked', state); - if (state) { - //Set the Rails hidden field that fakes an HTTP verb - //properly for current state action. - form.find('input[name=_method]').val('delete'); - span.html(form.attr('data-present')); - } else { - form.find('input[name=_method]').val('put'); - span.html(form.attr('data-absent')); - } - } + updateStateFor(state) { + this.checkboxTarget.checked = state - form.append(checkboxDiv); - updateStateFor(checked); - - checkbox.click(function() { - span.html(form.attr('data-inprogress')); - label.attr('disabled', 'disabled'); - checkbox.attr('disabled', 'disabled'); - - $.ajax({ - url: form.attr('action'), - beforeSend: function(xhr) { xhr.setRequestHeader('X-CSRF-Token', Blacklight.csrfToken()) }, - dataType: 'json', - type: form.attr('method').toUpperCase(), - data: form.serialize(), - error: function() { - label.removeAttr('disabled'); - checkbox.removeAttr('disabled'); - options.error.call(); - }, - success: function(data, status, xhr) { - //if app isn't running at all, xhr annoyingly - //reports success with status 0. - if (xhr.status != 0) { - checked = ! checked; - updateStateFor(checked); - label.removeAttr('disabled'); - checkbox.removeAttr('disabled'); - options.success.call(form, checked, xhr.responseJSON); - } else { - label.removeAttr('disabled'); - checkbox.removeAttr('disabled'); - options.error.call(); - } - } - }); - - return false; - }); //checkbox.click - - - }); //this.each - return this; - }; - - $.fn.blCheckboxSubmit.defaults = { - //cssClass is added to elements added, plus used for id base - cssClass: 'blCheckboxSubmit', - error: function() { - alert("Error"); - }, - success: function() {} //callback - }; -})(jQuery); + if (state) { + this.labelTarget.classList.add('checked') + //Set the Rails hidden field that fakes an HTTP verb + //properly for current state action. + this.formTarget.querySelector('input[name=_method]').value = 'delete' + this.spanTarget.innerHTML = this.form.getAttribute('data-present') + } else { + this.labelTarget.classList.remove('checked') + this.formTarget.querySelector('input[name=_method]').value = 'put' + this.spanTarget.innerHTML = this.form.getAttribute('data-absent') + } + } +}