In my Rails app, I have a form
for voting on various "post" titles. The form has one select
element, and submits remotely to a VotesController
using rails-ujs
. I want to handle response the normal way, in the controller receiving the request, with respond_to do |format|
.
One option is adding {:onchange=>'$(this.form).submit();'}
to the f.select
builder, as other answers have suggested.
However, because form submit takes about 8 seconds, I'd like to add my own handler to either the select
onchange
, or the form
onsubmit
(which event doesn't matter to me) to
- disable all other forms on the same page
- show a modal saying why we're disabling the other forms
- only allow one "favorite" vote per user (downgrade previous votes on other titles)
- store some data attributes in the select for later handling
The issue i'm having is
- if I bind my extra handler to the select
change
event, i can fire a submit via nativerails-ujs
, but the browser asks me if I want to leave the current page. I can solve this by adding my own AJAX call to my handler , but I'm trying to use the default Rails controllers andrails-ujs
to handle submit. I just want to add an extra handler to change the DOM before the slow response arrives. - if I bind my extra handler to the form
submit
event, the form does not submit and the binding does not have any effect on elements in the DOM, although there are no console errors.
Here's my code:
# partial appears on PostsController#show
# there are several of these on the page, for voting on post titles
<%= form_with(model: @vote, url: naming_vote_path(naming_id: naming.id),
method: :patch, local: false, id: "cast_vote_#{naming.id}",
class: "naming-vote-form") \
do |f| %>
<%= f.select(:value, menu, {},
{ class: "form-control w-100",
# onchange: "this.form.submit();", # works but does not allow further bindings
data: { role: "change_vote", id: naming.id } }) %>
<% end %>
$(document).ready(function () {
var change_vote_selects = function () {
return $("[data-role='change_vote']");
};
var attach_bindings = function () {
change_vote_selects().on("change", function (event) {
var _this = $(this);
var value = _this.val();
var naming_id = _this.data("id");
_haveVotesChanged = true;
// If setting vote to 3.0, go through all the rest and downgrade any
// old 3.0's to 2.0. Only one 3.0 vote is allowed. Also disable all
// the selects while the AJAX request is pending.
if (value == "3.0") {
change_vote_selects().each(function () {
var _this2 = $(this);
if (_this2.data("id") != naming_id && _this2.val() == "3.0") {
_this2.val("2.0");
}
_this2.attr("disabled", "disabled");
});
}
// modal printed in layout already, add text to it and activate
$('#naming_ajax_progress_caption').empty().append(
$("<span>").text(translations.show_namings_saving + "... "),
$("<span class='spinner-right mx-2'></span>")
);
$("#naming_ajax_progress").modal('show');
_this.parent().submit();
});
// Save initial value in case of error, when we'll need to revert.
change_vote_selects().each(function (event) {
var _this = $(this);
_this.data("old_value", _this.val());
_this.attr("disabled", null);
});
};
// Alert the user if they haven't saved data.
window.onbeforeunload = function () {
if (_haveVotesChanged && !_haveVotesBeenSaved)
return translations.show_namings_lose_changes;
}
attach_bindings();
});