Scott Anderson - a developer

Idiosyncrasies of the Magento 2 JS message system

·

The problem

Recently, I have been trying to convert some Product-rendering UI logic from a server-side php approach to a client-side JS UiComponent approach in Magento 2.3 EE.

At one point I started trying to trigger Magento 2 error messages from PHP to JS-triggered due to the fact that product data delivery was now changed to delivery via AJAX request. I made an M2 JSON controller which accepted POST requests only:

namespace Vendor\Module\Controller\Action;

use Magento\Framework\App\Action;

class JSONController extends Action implements HttpPostActionInterface
{
  /**
   * @var JsonResultFactory
  protected $jsonResultFactory;

  //some props

  public function __construct(
    JsonResultFactory $jsonResultFactory
    //some props
  )
  {
    //some init
  }

  public function execute()
  {
    return $this->jsonResultFactory->create()
      ->setResponseCode(200)
      ->setData(['some' => 'data']);
  }
}

I called this controller via jQuery AJAX

define([
  //some modules
  ], function () {
    return Element.Extend({
    ...
    getProductData: function() {
      return $.ajax({
        accept: 'application/json',
        action: 'POST',
        url: 'module/action/jsoncontroller'
        data: {
          productGroup: selfProductGroup
        }
      }, self);
    }
    ...
  });

All looks good - after some tweaking I got the data I wanted on the frontend. Google-ing for the correct way to implement messages ensued.

Then I exhausted all my options, and nothing worked.

So I left it for a moment, but whilst debugging an issue with data return I noticed that my error message was appearing when I was debugging the AJAX return in the debugger. As soon as the AJAX ‘fail’ method was completed however it was gone. How can this be?!

At the time I was using the following code in my UiComponent:

                customerData.set('messages', {
                    messages: [{
                        text: $t('There\'s been an error.'),
                        type: 'error'
                    }]
                });

…but I am sure other solutions offered on SO (as per links above would work).

But the important part for me was in Magento (2.3.1) core code, namely vendor/magento/module-customer/view/frontend/web/js/customer-data.js::392

    /**
     * Events listener
     */
    $(document).on('ajaxComplete', function (event, xhr, settings) {
        var sections,
            redirects;

        if (settings.type.match(/post|put|delete/i)) {
            sections = sectionConfig.getAffectedSections(settings.url);

            if (sections) {
                customerData.invalidate(sections);
                redirects = ['redirect', 'backUrl'];

                if (_.isObject(xhr.responseJSON) && !_.isEmpty(_.pick(xhr.responseJSON, redirects))) { //eslint-disable-line
                    return;
                }
                customerData.reload(sections, true);
            }
        }
    });

This code means that more-or-less ANY post request using the jQuery .ajax method*, will cause all of the messages you have added via JS to reset.

* Unless it does not create any ‘affected sections’ - you will have to inspect the code yourself to see what I mean. Regardless I believe is an unlikely case