Adding a new widget in Magento 2

Magento 2 has a handy toolkit for static content management on the site that is called Content Management System (CMS). The toolkit consists of three main parts: pages, blocks and widgets. Pages allow us to manage content of an entire page. Blocks provide an ability to edit content of separate page elements. The main difference between blocks and widgets: if you need to change block parameters, you usually have to do it programmatically. Widgets are similar to static blocks, they allow to insert various content into static pages or static blocks. As a rule, widgets have configurable parameters that can be set up when adding it via admin panel.

Magento has a set of widgets that may be used out of the box. In order to add a widget to a page or a block, go to the page/block edit tab in admin panel, then click “Add Widget” button in WYSIWYG editor of the “Content” field. In the newly opened popup you are able to choose what widget to insert and configure parameters of the widget. For example, if you choose “Recently Viewed Products” widget, you will be able to choose a number of products to display and one of the predefined parameters. We are talking about the standard set of widgets but there are many situations where it’s very handy to have a custom widget that allows you to display certain information in different places with a variety of options.

For example, let’s review a simple widget that allows you to add a block with contact information: name, gender, phone, email. Using this block, we can display different contact data.

To add a new widget we can use a simple custom extension called Atwix_Widget. To initialize a new Magento 2 extension we need 3 files: etc/module.xml, registration.php and composer.json with the following content:

<?xml version="1.0"?>

<?xml version="1.0"?>

<!-- file: app/code/Atwix/Widget/etc/module.xml -->

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Atwix_Widget" setup_version="1.0.0">
    </module>
</config>
/* file: app/code/Atwix/Widget/registration.php */

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Atwix_Widget',
    __DIR__
);
/* file: app/code/Atwix/Widget/composer.json */
{
  "name": "atwix/widget",
  "description": "N/A",
  "require": {
    "php": "~5.5.0|~5.6.0|~7.0.0"
  },
  "type": "magento2-module",
  "version": "1.0.0",
  "license": [
    "OSL-3.0",
    "AFL-3.0"
  ],
  "autoload": {
    "files": [ "registration.php" ],
    "psr-4": {
      "Atwix\\Widget\\": ""
    }
  }
}

After this, we need to add an additional XML configuration for our new widget. The file that is responsible for widgets in Magento is placed in [module_dir]/etc/widget.xml. So, we will add this file to our own extension with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<widgets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Widget:etc/widget.xsd">
    <widget id="atwix_contact" class="\Atwix\Widget\Block\Widget\ContactWidget">
        <label translate="true">Contact Details</label>
        <description translate="true">Widget with editable contact details</description>
        <parameters>
            <parameter name="fullname" xsi:type="text" visible="true" sort_order="10">
                <label translate="true">Full Name</label>
            </parameter>
            <parameter name="gender" xsi:type="select" required="true" visible="true"  sort_order="20">
                <label translate="true">Gender</label>
                <options>
                    <option name="male" value="male" selected="true">
                        <label translate="true">Male</label>
                    </option>
                    <option name="female" value="female">
                        <label translate="true">Female</label>
                    </option>
                </options>
            </parameter>
            <parameter name="phone" xsi:type="text" visible="true" sort_order="30">
                <label translate="true">Phone Number</label>
            </parameter>
            <parameter name="email" xsi:type="text" visible="true" sort_order="40">
                <label translate="true">Email</label>
            </parameter>
            <parameter name="template" xsi:type="select" required="true" visible="false">
                <label translate="true">Template</label>
                <options>
                    <option name="default" value="contact.phtml" selected="true">
                        <label translate="true">New Products Grid Template</label>
                    </option>
                </options>
            </parameter>
        </parameters>
    </widget>
</widgets>

As you can see from the configuration, each widget has its own unique ID, in our case it’s “atwix_contact”. Also, it has a block class that will be used to render our widget. In this example we use an empty block but it can contain logic that should be accessible from its template.

<?php

/* file: app/code/Atwix/Widget/Block/Widget/ContactWidget.php */

namespace Atwix\Widget\Block\Widget;

use Magento\Framework\View\Element\Template;
use Magento\Widget\Block\BlockInterface;

class ContactWidget extends Template implements BlockInterface
{

}

Within the widget’s scope, there’s a list of available parameters. Each parameter has its own options: visibility (show or hide this field in the admin panel), sort order (where to show in the admin panel among other parameters) and parameter type. The last one depends on how we are going to show this parameter in the admin panel: text – standard input field; select – dropdown with values, etc. You can also create your own input type, the good example is Magento\Catalog\Block\Adminhtml\Product\Widget\Chooser type. Each widget parameter has its own label that can be translated using built-in internationalization tools.

To display our widget in an appropriate format we also need to create a template with some HTML inside. Also, we need to somehow assign the template to our widget. Usually, we can do it using a setTemplate() method inside our block class but in this case we assign our template to the widget by passing it as a widget parameter with name “template”.

The template itself will have the following content:

<!-- file: app/code/Atwix/Widget/view/frontend/templates/contact.phtml -->

<div class="atwix-contacts">
    <div class="name">Name: <?php echo $block->getData('fullname'); ?></div>
    <div class="name">Gender: <?php echo $block->getData('gender'); ?></div>
    <div class="phone">Phone: <?php echo $block->getData('phone'); ?></div>
    <div class="email">Email: <?php echo $block->getData('email'); ?></div>
</div>

To enable the extension you need to run the following commands in your shell (from Magento installation root directory):

./bin/magento cache:flush ; \ 
./bin/magento module:enable Atwix_Widget ; \
./bin/magento setup:upgrade

Now, you can navigate to the admin panel, open any CMS page or CMS block, and insert your new widget called “Contact Details”. Don’t forget to flush cache after having saved your changes. Feel free to share your thought in the comments below. Good luck with your Magento 2 widget customizations!

You may also want to read: