I have a form on an ASP.NET Core Razor Pages page. If I add a submit button (type="submit"
) to the form, the form posts normally as expected.
If I change the submit button to a regular button (type="button"
) and add a handler for that button that calls .trigger('submit')
, it also posts normally as expected.
$('#myButton').on('click', function () {
$('#myForm').trigger('submit');
});
However, if I change the handler makes an $ajax()
call as shown below, and trigger the form submit from within the done
handler, I get a 400 error.
$('#myButton').on('click', function () {
var $form = $(this).closest('form');
$.ajax({
type: 'GET',
url: '?handler=ValidateProductTransfer',
contentType: 'application/json',
dataType: 'json',
data: { ... },
})
.done(function (response) {
if (response.isSuccess)
$form.trigger('submit'); // <----- FAILS w/400 error!
else
alert(response.error);
})
.fail(function (response) {
alert(response.responseText);
})
});
I can workaround the issue above by delaying the form submit.
.done(function (response) {
if (response.isSuccess)
setTimeout(function () {
$form.trigger('submit'); // <----- OK
}, 300);
else
alert(response.error);
})
Obviously, there is an issue triggering a form submit in the $ajax().done
handler. But I don't understand why, or why it results in a 400 error.
Can anyone explain what is happening here? And how to work around it without imposing a delay that could fail if the delay is not enough?
Update
If it helps, here is my actual JavaScript handler.
$('#product-transfer-submit').on('click', function () {
disablePopup(true);
var $form = $(this).closest('form');
var $modal = $('#transfer-product-modal');
var $activeTab = $modal.find('a.nav-link.active');
var targetType = $activeTab.data('type');
var targetId = $modal.find($activeTab.attr('href') ' :input').val();
$modal.find('#TargetType').val(targetType);
$.ajax({
type: 'GET',
url: '?handler=ValidateProductTransfer',
contentType: 'application/json',
dataType: 'json',
data: {
'sourceId': $modal.find('#SourceId').val(),
'targetType': targetType,
'targetId': targetId,
'productId': $modal.find('#ProductId').val()
},
})
.done(function (response) {
if (response.isSuccess)
//setTimeout(function () {
$form.trigger('submit');
//}, 300);
else
alert(response.error);
})
.fail(function (response) {
alert(response.responseText);
})
.always(function (response) {
disablePopup(false);
});
});
function disablePopup(disable) {
$("#transfer-product-modal :input").prop("disabled", disable);
$("#transfer-product-modal a").prop("disabled", disable);
}
And here's my complete form as rendered in the browser.
<div id="transfer-product-modal" tabindex="-1" role="dialog" aria-modal="true" style="display: block;">
<form method="post" action="/Transloading/Ships?handler=ProductTransfer" novalidate="novalidate">
<input type="hidden" data-val="true" data-val-required="The SourceId field is required." id="SourceId" name="SourceId" value="2">
<input type="hidden" data-val="true" data-val-required="The TargetType field is required." id="TargetType" name="TargetType" value="Ship">
<div role="document">
<div >
<div >
<h5 >Ship 2 : Transfer Product To</h5>
<button type="button" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div >
<ul >
<li >
<a href="#railcar-content" data-bs-toggle="tab" aria-expanded="false" data-type="railcar">
<span ><img src="/images/mdi/calendar.svg" title="Railcar"></span>
<span >Railcar</span>
</a>
</li>
<li >
<a href="#storage-content" data-bs-toggle="tab" aria-expanded="false" data-type="storage">
<span ><img src="/images/mdi/calendar.svg" title="Storage"></span>
<span >Storage</span>
</a>
</li>
<li >
<a href="#ship-content" data-bs-toggle="tab" aria-expanded="false" data-type="ship">
<span ><img src="/images/mdi/calendar.svg" title="Ships"></span>
<span >Ship</span>
</a>
</li>
</ul>
<div >
<div id="railcar-content" >
<div >
<div >
<div >
<label for="RailcarId">Railcar</label>
<select data-val="true" data-val-required="The Railcar field is required." id="RailcarId" name="RailcarId"><option value="36167">AOKX482582</option>
<option value="36400">AOKX491641</option>
<option value="36399">AOKX497959</option>
<option value="36240">CBFX305181</option>
<option value="36245">TOPX311252</option>
<option value="36193">TOPX311288</option>
<option value="36244">TOPX311390</option>
</select>
<span data-valmsg-for="RailcarId" data-valmsg-replace="true"></span>
</div>
</div>
</div>
</div>
<div id="storage-content" >
<div >
<div >
<div >
<label for="StorageId">Storage</label>
<select data-val="true" data-val-required="The Storage field is required." id="StorageId" name="StorageId"><option value="15">Test Storage (100 Mesh)</option>
</select>
<span data-valmsg-for="StorageId" data-valmsg-replace="true"></span>
</div>
</div>
</div>
</div>
<div id="ship-content" >
<div >
<div >
<div >
<label for="ShipId">Ship</label>
<select data-val="true" data-val-required="The Ship field is required." id="ShipId" name="ShipId"><option value="1">Ship 1</option>
<option value="2">Ship 2</option>
</select>
<span data-valmsg-for="ShipId" data-valmsg-replace="true"></span>
</div>
</div>
</div>
</div>
</div>
<div >
<div >
<div >
<label for="ProductId">Product</label>
<select data-val="true" data-val-required="The Product field is required." id="ProductId" name="ProductId"><option value="1">100 Mesh</option><option value="18">Butane</option><option value="19">Diesel</option><option value="40">Propane</option></select>
<span data-valmsg-for="ProductId" data-valmsg-replace="true"></span>
</div>
</div>
</div>
<div >
<div >
<div >
<label for="Quantity">Quantity</label>
<input type="text" data-val="true" data-val-number="The field Quantity must be a number." data-val-range="Quantity must be a positive number" data-val-range-max="1000000000" data-val-range-min="0.1" data-val-required="The Quantity field is required." id="Quantity" name="Quantity" value="0">
<span data-valmsg-for="Quantity" data-valmsg-replace="true"></span>
</div>
</div>
<div >
<div >
<label for="Timestamp">Timestamp</label>
<input type="datetime-local" data-val="true" data-val-required="The Timestamp field is required." id="Timestamp" name="Timestamp" value="0001-01-01T00:00"><input name="__Invariant" type="hidden" value="Timestamp">
<span data-valmsg-for="Timestamp" data-valmsg-replace="true"></span>
</div>
</div>
</div>
<div >
<div >
<div >
<input type="checkbox" data-val="true" data-val-required="The Mark source as empty field is required." id="MarkAsEmpty" name="MarkAsEmpty" value="true">
<label for="MarkAsEmpty">Mark source as empty</label>
</div>
</div>
</div>
</div>
<div >
<button type="button" id="product-transfer-submit" >Transfer</button>
<button type="button" data-bs-dismiss="modal">Cancel</button>
</div>
</div>
</div>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8BlcV1FRqxZOunKbIAf54ZLanzQR8IEs7WCpxJ-3rTLTtnUwgcrJW0eL-Tl3Z_3exdKwVNmoPzo3m644U1W1i1nwO3iJDvZVW6EXTKkrdGHZJPh_QkKWU_6k5wGPNDpDzPwVOAK8p7CmxDE2QzacCwz3rUcnjpXhyrhWhup0xq7e6n50j0vA3TBlBKR2bxfOzA"><input name="MarkAsEmpty" type="hidden" value="false">
</form>
</div>
CodePudding user response:
The issue is your disablePopup()
function and when it is called.
Disabled inputs are not included in submitted data. The reason the timeout works is because it gives your .always()
callback a chance to re-enable the inputs.
You can alter the order of your deferred callbacks to ensure you re-enable the inputs before processing success or failure...
$.ajax({
url: "",
method: "GET",
data: {
targetType,
targetId,
handler: "ValidateProductTransfer",
sourceId: $modal.find("#SourceId").val(),
productId: $modal.find("#ProductId").val(),
},
dataType: "json",
})
.always(() => { //