Magento 2 offers a highly advanced frontend development system. One of its components is JQuery, which is available by default. It is the base of many JavaScript widgets and makes the work with Magento 2 frontend much easier.

There’s no need to write another dropdown script, we can use the one built into Magento. We can find the examples of how to use it in the Magento_Store module templates.

//file: vendor/magento/module-store/view/frontend/templates/switch/languages.phtml

        <ul class="dropdown switcher-dropdown"
            data-mage-init='{"dropdownDialog":{
                "appendTo":"#switcher-language<?php /* @escapeNotVerified */ echo $id ?> > .options",
                "triggerTarget":"#switcher-language-trigger<?php /* @escapeNotVerified */ echo $id ?>",
                "closeOnMouseLeave": false,
                "triggerClass":"active",
                "parentClass":"active",
                "buttons":null}}'>
            <?php foreach ($block->getStores() as $_lang): ?>
                <?php if ($_lang->getId() != $block->getCurrentStoreId()): ?>
                    <li class="view-<?php echo $block->escapeHtml($_lang->getCode()); ?> switcher-option">
                        <a href="#" data-post='<?php /* @escapeNotVerified */ echo $block->getTargetStorePostData($_lang); ?>'>
                            <?php echo $block->escapeHtml($_lang->getName()) ?></a>
                    </li>
                <?php endif; ?>
            <?php endforeach; ?>
        </ul>

There’s a lot we can customize. For example, by using appendTo we can change the location of the code with the dropdown. Setting the closeOnMouseLeave to true will cause our dropdown to close when we leave it. There are more default options – they can be found in the vendor/magento/magento2-base/lib/web/mage/dropdown.js file. The effect will be visible when we add a new Store View.

Custom widget with AJAX

As you can see with the dropdown example, we can easily initiate widgets wherever we need them.
In order to illustrate the way widgets are created and how they work, let’s make a simple widget to handle an AJAX query – in our case, after clicking the button (for example, we can let the client obtain additional information about the product). In this scenario we’ll create a controller responsible for both AJAX and the js widget that will manage the query.

//file: app/code/Magently/Ajax/registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Magently_Ajax',
    __DIR__
);
//file: app/code/Magently/Ajax/etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Magently_Ajax" setup_version="0.1.0">
    </module>
</config>

Next, let’s add the controller to handle our query.
First, we need to add the xml defining our controller:

//file app/code/Magently/Ajax/etc/frontend/routes.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="magently_ajax" frontName="magently">
            <module name="Magently_Ajax" />
        </route>
    </router>
</config>

We set the frontName here as magently, so the controller classes created will be available at {{BASE_URL}}/magently/controller
Let’s add a class responsible for our controller now:

//file: app/code/Magently/Ajax/Controller/Query/Custom.php

<?php
namespace Magently\Ajax\Controller\Query;

class Custom extends \Magento\Framework\App\Action\Action
{
    public function execute()
    {
        //get data
        $data = [
            'key' => 'value',
            'key2' => 'value2'
        ];

        var_dump($data);
    }
}

When we go to {{base_url}}/magently/query/custom, we’ll see a dumped $data variable – it’s a sign our controller works.
Next, let’s adjust it for AJAX. We simply need to render the data as json, so that javascript could read it.

//file: app/code/Magently/Ajax/Controller/Query/Custom.php

...
    public function execute()
    {
        //get data
        $data = [
            'key' => 'value',
            'key2' => 'value2'
        ];

        /** @var \Magento\Framework\Controller\Result\Json $resultJson */
        $resultJson = $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_JSON);
        $resultJson->setData($data);
        return $resultJson;
    }
...

When we go to the above address again, we’ll see the json object, so everything works.
It’s time to make the widget now. The example is based on binding an element and submitting an AJAX query to our controller. We’ll also use to built-in Magento loader.
First, let’s create the file with the widget:

//file: app/code/Magently/Ajax/view/frontend/web/js/magently-js.js

define([
    "jquery"
], function($) {
    "use strict";
    $.widget('magently.ajax', {
        options: {
            url: 'magently_ajax/getInfo',
            method: 'post',
            triggerEvent: 'click'
        },

        _create: function() {
            this._bind();
        },

        _bind: function() {
            var self = this;
            self.element.on(self.options.triggerEvent, function() {
                self._ajaxSubmit();
            });
        },

        _ajaxSubmit: function() {
            var self = this;
            $.ajax({
                url: self.options.url,
                type: self.options.method,
                dataType: 'json',
                beforeSend: function() {
                    console.log('beforeSend');
                    $('body').trigger('processStart');
                },
                success: function(res) {
                    console.log('success');
                    console.log(res);
                    $('body').trigger('processStop');
                }
            });
        },

    });

    return $.magently.ajax;
});

We make the new widget using jQuery. The options object contain variables that we can refer to in the widget via this.options.optionName. We can also overwrite these options from the widget launch level – we’ll see this in the next steps. The _create() method is triggered every time we trigger the widget. We use it to launch the _bind() method, which will in turn launch the _ajaxSubmit() once we execute triggerEvent.

The _ajaxSubmit() method processes our query. In the beforeSend callback we trigger processStart to body. Thanks to this, the loader will be displayed before sending. In the callback, we stop success using event processStop. Other options for the jQuery AJAX can be found in the documentation.

At the end of the script, we trigger our newly created widget.
Let’s add our script to the requirejs config:

//file: app/code/Magently/Ajax/view/frontend/requirejs-config.js

var config = {
    map: {
        '*': {
            magentlyJs: 'Magently_Ajax/js/magently-js',
        }
    }
};

Template

We will turn our widget on from the level of the template that we’ll add to the product page.
We need to create a template file with a widget launcher:

//file: app/code/Magently/Ajax/view/frontend/templates/info.phtml

<div class="ajax-info">
    <button id="ajax-info-action" type="button">Get Ajax Info</button> 
</div>

<script type="text/x-magento-init">
{
    "#ajax-info-action":
    {
        "Magently_Ajax/js/magently-js": { } 
    } 
}
</script>

It is a simple button that we assign our widget to. We can access it in the widget using this.element.
As we mentioned before, we can overwrite the default options (in this case we change the default triggerEvent from click to mouseenter):

//file: app/code/Magently/Ajax/view/frontend/templates/info.phtml

...
<script type="text/x-magento-init">
{
    "#ajax-info-action":
    {
        "Magently_Ajax/js/magently-js":
        {
            "triggerEvent": "mouseenter"
        } 
    } 
}
</script>
...

Let’s add our block to the product page:

//file: app/code/Magently/Ajax/view/frontend/layout/catalog_product_view.xml

<?xml version="1.0"?>
<page layout="1column" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="product.info.main">
                <block class="Magento\Catalog\Block\Product\View" name="magently.ajax.info" template="Magently_Ajax::info.phtml" after="product.info" />
        </referenceContainer>
    </body>
</page>

We add the magently.ajax.info block to the product.info.main container.
Now we can see our button on the product page. When we click on it, our AJAX will be executed:

We hope you’ll find our article helpful! In case you have any questions, please leave a comment below.