How to create custom jQuery Widget using Magento 2?

You can simply create jQuery widget using Magento 2 by just simple steps. Using jQuery widget you can pass dynamic value from php to js file.

Many times you need to pass dynamic value using phtml file to js file, In this scenario, you can help jQuery widget for getting dynamic value from a template or phtml file.

I have just created simple module for better understanding of widget. You can see many places inthe core code of magento which used jQuery widget.

Lets start with simple module, Rbj_DemoWidget,  Create registration.php file for our module register,
File path, app/code/Rbj/DemoWidget/registration.php

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Rbj_DemoWidget',
    __DIR__
);

Create module.xml file for configure our module entry to setup_module table.
File path, app/code/Rbj/DemoWidget/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="Rbj_DemoWidget" setup_version="1.0.0">
    </module>
</config>

We have create one front action for call our template, Create one routes.xml file,

File path, app/code/Rbj/DemoWidget/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="demo" frontName="demo">
            <module name="Rbj_DemoWidget" />
        </route>
    </router>
</config>

In our routes.xml file, We have set frontname as demo. so we can access our template using storeUlr/demo/ControllerfoldeName/actionPhp
Now we create Controller action file called Index.php file,
File path, app/code/Rbj/DemoWidget/Controller/Index/Index.php

<?php
namespace Rbj\DemoWidget\Controller\Index;

class Index extends \Magento\Framework\App\Action\Action
{
    /**
     * Index action
     *
     * @return $this
     */
    public function execute()
    {
        $this->_view->loadLayout();
        $this->_view->getLayout()->getBlock('page.main.title')->setPageTitle('DemoWidget');
        $this->_view->renderLayout();
    }
}

In controller, we have just set page title only and load and render layout for render our layout.
Our final Url will be STORE_URL/demo/index/index

Create our action handle in layout folder,
File path, app/code/Rbj/DemoWidget/view/frontend/layout/demo_index_index.xml
XML file demo_index_index.xml,

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block class="Magento\Framework\View\Element\Template" name="demo.page" template="Rbj_DemoWidget::magento-widget.phtml"></block>
        </referenceContainer>
    </body>
</page>

File path, app/code/Rbj/DemoWidget/view/frontend/requirejs-config.js
Create requirejs-config.js file for our js declaration,

var config = {
    "map": {
        "*": {
            "myCustomWidget": "Rbj_DemoWidget/js/my-custom-widget"
        } 
    }
};

Our widget name will be myCustomWidget.

File path, app/code/Rbj/DemoWidget/view/frontend/web/js/my-custom-widget.js
Create my-custom-widget.js file for declaring our widget,

define([
    'jquery',
    'jquery/ui'
    ], function($){
        $.widget('mage.myCustomWidget', {
            options: {
                abcd: 1,
                passvalue:'test'
            },
            /**
             * Widget initialization
             * @private
             */
             _create: function() {
			    alert(this.options.passvalue);	custom message
			    alert(this.options.abcd); // 123
            }
        });

    return $.mage.myCustomWidget;
});

In above js file, We have created the custom widget in Magento 2, Here you must need to pass jquery/ui as dependencies for call our custom widget in any phtml file.

_create function is used for call our default method and variable when our widget initialized. You can pass dynamic variable from the template. Here options object variable will be changed based on a value of template file.

Create template magento-widget.phtml file,File path, app/code/Rbj/DemoWidget/view/frontend/templates/magento-widget.phtml

<div class="maindiv">
    <div class="secondary">
        Widget Example using Magento 2
    </div>
<div> 
<script type="text/x-magento-init">
    {
        ".maindiv": {
            "myCustomWidget": {
                "passvalue": "custom message",
                "abcd": 123
            }
        }
    }
</script>

Here we will call our customWidget in a template file,
Pass variable from here to override js variable.
We have set variable name passvalue and value to override default js value.

So you can pass a custom dynamic value like the above example and override default value.

Final result,
passvalue variable default value is test and we can pass the custom message so test value will be overridden with the custom message
same for abcd variable default value 1 will override with 123.

When you run the front action,
You got alert with value of passvalue equals to custom message and  abcd value equals 123.

 

How to reindex indexer programmatically in magento 2?

Just create PHP file under your module’s Cron folder,
A created module like Rbj_Indexing for reference, Create Indexer.php file and call execute() function,

<?php
namespace Rbj\Indexing\Cron;

class Indexer
{
    public function __construct(
        \Magento\Indexer\Model\Processor $processor
    ) {
        $this->processor = $processor;
    }

    public function execute()
    {
        /* Regenerate indexes for all indexers */
        $this->processor->reindexAll();

        /* Regenerate indexes for all invalid indexers */
        $this->processor->reindexAllInvalid()
    }
}

There are two types of method for indexing.

1. Regenerate indexes for all indexers
$this->processor->reindexAll();

2. Regenerate indexes for all invalid indexers. This will only regenerate for invalid indexer.
$this->processor->reindexAllInvalid()

How to override template (.phtml) files in magento 2?

Override template files in Magento 2 is common and just a simple. You can override template file by below ways.
1. Using Theme level in the theme directory
2. Using Module level in the custom module

1. Using Theme level,
We will discuss first theme level override templates. I hope you have created theme for your site. If you don’t know how to create theme refer to link for creating the custom theme,
For theme level override you must have a custom theme in your project.
Let’s assume We need to override list.phtml file from Catalog core module.
Path for core module is vendor/magento/module-catalog/view/frontend/templates/product/list.phtml

We need to create Magento_Catalog folder in our theme root directory.
Let’s assume your theme name is Vendorname/themename. Your actual path for a theme is,
app/design/frontendVendorname/themename. You need to create Folder Magento_Catalog for override catalog module template file. Our core module template resides in module-catalog folder so you need to create Core module folder like,
Magento_Catalog (Where Magento is Vendorname and Modulename will be in UpperCamelCase letter with suffix “prefix”)

Now you can override your list.phtml at below location,,
app/design/frontendVendorname/themename/Magento_Catalog/templates/product/list.phtml
Create list.phtml file and get content from core Magento Catalog template list.phtml file.

For some core module name in Magento 2 has multiple words separated with the hyphen(-).
Like, module-configurable-product, In this case, our Module name in the theme will be, Magento_ConfigurableProduct
if you want to override templates from module-checkout-agreements your theme level folder will be Magento_CheckoutAgreements

2. Using Module Level,
You can override template file using the Module level.
You need to create layout XML file from your controller action,
app/code/<Vendor>_<Module>/view/frontend/layout/catalog_category_view.xml

(Using New method)

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="category.products.list">
            <arguments>
                <argument name="template" xsi:type="string">Vendor_Module::product/list.phtml</argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

(Using old deprecated method, action method=”setTemplate”)

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="category.products.list">
            <action method="setTemplate">
                <argument name="template" xsi:type="string">Vendor_Module::product/list.phtml</argument>
            </action>
        </referenceBlock>
    </body>
</page>

Using New method some of the templates is still not overridden check link, Github Issue.

Do not use <action> tag, if the method implementation allows calling it using <argument> for block or referenceBlock.

For example, in your module, you would put a list template in  module for path, app/code/<Vendor>_<Module>/view/frontend/templates/product/list.phtml

Conclusion:

Which method should we use from the above solutions?
There is no one specific method that should be used in all scenarios.
If you are creating a theme, use the template path method(Method 1 is more suitable for frontend developer).
If you are creating a module, use the layout at the module level and define your template.