I have an issue that has been confounding me. I recently upgraded Magento from 2.4.3 to 2.4.6. I have a custom form that since then has been redirecting to the home page rather than submitting, but only in Safari. I ruled out CSP issues, Form Key issues, and have no errors in the console. I discovered that if I put a breakpoint in the _save
function in client.js
(which is a file generated by Magento) the form operates correctly. This leads me to believe it is a timing issue, but since it is generated and handled by Magento I am not sure how to fix it.
The code is below:
Magento’s client.js
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
define([
'jquery',
'underscore',
'mageUtils',
'uiClass'
], function ($, _, utils, Class) {
'use strict';
/**
* Before save validate request.
*
* @param {Object} data
* @param {String} url
* @param {String} selectorPrefix
* @param {String} messagesClass
* @returns {*}
*/
function beforeSave(data, url, selectorPrefix, messagesClass) {
var save = $.Deferred();
data = utils.serialize(utils.filterFormData(data));
data['form_key'] = window.FORM_KEY;
if (!url || url === 'undefined') {
return save.resolve();
}
$('body').trigger('processStart');
$.ajax({
url: url,
data: data,
/**
* Success callback.
* @param {Object} resp
* @returns {Boolean}
*/
success: function (resp) {
if (!resp.error) {
save.resolve();
return true;
}
$('body').notification('clear');
$.each(resp.messages || [resp.message] || [], function (key, message) {
$('body').notification('add', {
error: resp.error,
message: message,
/**
* Insert method.
*
* @param {String} msg
*/
insertMethod: function (msg) {
var $wrapper = $('<div></div>').addClass(messagesClass).html(msg);
$('.page-main-actions', selectorPrefix).after($wrapper);
$('html, body').animate({
scrollTop: $('.page-main-actions', selectorPrefix).offset().top
});
}
});
});
},
/**
* Complete callback.
*/
complete: function () {
$('body').trigger('processStop');
}
});
return save.promise();
}
return Class.extend({
/**
* Assembles data and submits it using 'utils.submit' method
*/
save: function (data, options) {
var url = this.urls.beforeSave,
save = this._save.bind(this, data, options);
beforeSave(data, url, this.selectorPrefix, this.messagesClass).then(save);
return this;
},
/**
* Save data.
*
* @param {Object} data
* @param {Object} options
* @returns {Object}
* @private
*/
_save: function (data, options) {
var url = this.urls.save;
$('body').trigger('processStart');
options = options || {};
if (!options.redirect) {
url += 'back/edit';
}
if (options.ajaxSave) {
utils.ajaxSubmit({
url: url,
data: data
}, options);
$('body').trigger('processStop');
return this;
}
utils.submit({
url: url,
data: data
}, options.attributes);
return this;
}
});
});
RecurringService/view/frontend/templates/pageactions.phtml
<?php declare(strict_types=1);
use RecurringServiceViewModelFormKey;
/** @var FormKey $viewModel */
$viewModel = $block->getViewModel();
?>
<?php if ($block->getChildHtml()) : ?>
<script>
var FORM_KEY = '<?= /* @noEscape */ $viewModel->getFormKey() ?>';
</script>
<div>
<?= $block->getChildHtml() ?>
</div>
<?php endif; ?>
RecurringService/view/frontend/layout/recurring_schedule_index.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<update handle="customer_account"/>
<body>
<referenceBlock name="page.main.title">
<action method="setPageTitle">
<argument translate="true" name="title" xsi:type="string">My Service Schedule</argument>
</action>
</referenceBlock>
<referenceContainer name="content">
<block class="MagentoCmsBlockBlock" name="service_schedule_description">
<arguments>
<argument name="block_id" xsi:type="string">service-schedule-description</argument>
</arguments>
</block>
<uiComponent name="service_schedule_form"/>
<block class="MagentoFrameworkViewElementTemplate"
cacheable="false"
name="page.actions.toolbar" template="RecurringService::pageactions.phtml">
<arguments>
<argument name="view_model" xsi:type="object">RecurringServiceViewModelFormKey</argument>
</arguments>
</block>
</referenceContainer>
</body>
</page>
RecurringService/view/frontend/ui_component/service_schedule_form.xml
<?xml version="1.0"?>
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">service_schedule_form.service_schedule_form_data_source</item>
</item>
<item name="template" xsi:type="string">templates/form/collapsible</item>
</argument>
<settings>
<buttons>
<button name="save" class="RecurringServiceBlockServiceScheduleFormSaveButton"/>
</buttons>
<namespace>service_schedule_form</namespace>
<dataScope>data</dataScope>
<deps>
<dep>service_schedule_form.service_schedule_form_data_source</dep>
</deps>
</settings>
<dataSource name="service_schedule_form_data_source">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
</item>
</argument>
<settings>
<submitUrl path="recurring/schedule/save"/>
</settings>
<dataProvider class="RecurringServiceModelRecurringProfileDataProvider"
name="service_schedule_form_data_source">
<settings>
<requestFieldName>profile_id</requestFieldName>
<primaryFieldName>entity_id</primaryFieldName>
</settings>
</dataProvider>
</dataSource>
<fieldset name="service_schedule" class="RecurringServiceUiComponentFormServiceSchedule">
<settings>
<label translate="true">Service Schedule</label>
</settings>
</fieldset>
</form>
RecurringService/Block/ServiceSchedule/Form/SaveButton.php
<?php declare(strict_types=1);
namespace RecurringServiceBlockServiceScheduleForm;
use MagentoFrameworkViewElementUiComponentControlButtonProviderInterface;
/**
* Class SaveButton
*/
class SaveButton implements ButtonProviderInterface
{
/**
* Gets button data
*
* @return mixed[]
*/
public function getButtonData()
{
return [
'label' => __('Update Service Schedule'),
'class' => 'save primary',
'data_attribute' => [
'mage-init' => [
'buttonAdapter' => [
'actions' => [
[
'targetName' => 'service_schedule_form.service_schedule_form',
'actionName' => 'save'
]
]
]
]
]
];
}
}