Tag: magento2

KnockoutJs custom bindings in Magento 2

Today I will discuss KnockoutJs two important custom bindings in Magento 2. This is the series of Magento 2 Certified Professional Javascript Developer exam.

Two important knockoutjs bindings are:

1. Knockout Fast Foreach
2. Repeat

How these bindings are the map in magento 2?
vendor/magento/module-ui/view/base/web/js/lib/knockout/bindings/bootstrap.js

1. Knockout Fast Foreach
KnockoutJs default foreach is slow for large data. For overcome this slowness magento use Knockout Fast Foreach out of box. Location lib/web/knockoutjs/knockout-fast-foreach.js. Ex.

<div data-bind="scope: 'outerfasteach-example'">
    <table>
        <thead>
            <tr><th>First name</th><th>Last name</th></tr>
        </thead>
        <tbody data-bind='fastForEach: {data: getCustomerData()}'>
            <tr>
                <td data-bind="text: firstName"></td>
                <td data-bind="text: lastName"></td>
            </tr>
        </tbody>
    </table>
</div>
<script type="text/x-magento-init">
{
    "*": {
        "Magento_Ui/js/core/app": {
            "components": {
                "outerfasteach-example": {
                    "component": "VendorName_ModuleName/js/fast-foreach"
                }
            }
        }
    }
}
</script>

app/code/VendorName/ModuleName/view/frontend/web/js/fast-foreach.js

define([
    'ko',
    'uiComponent',
    'underscore'
], function (ko, Component, _) {
    'use strict';

    return Component.extend({
        customerNameList: ko.observableArray([]),
        /** @inheritdoc */
        initialize: function () {
            this._super();
            var self = this;
            for (var i = 0; i < 5000; i++) {
                self.customerNameList.push({'firstName': 'firstName_'+i, 'lastName': 'lastName_'+i});
            }
        },
        getCustomerData: function () {
            return this.customerNameList;
        }
    });
});

You can use as a node, so attribute name should be outerfasteach. Check here how magento uses in the grid listing:
vendor/magento/module-ui/view/base/web/templates/grid/listing.html

<div class="admin__data-grid-wrap" data-role="grid-wrapper">
    <table class="data-grid" data-role="grid">
       <thead>
            <tr each="data: getVisible(), as: '$col'" render="getHeader()"/>
        </thead>
        <tbody>
            <tr class="data-row" repeat="foreach: rows, item: '$row'" css="'_odd-row': $index % 2">
                <td outerfasteach="data: getVisible(), as: '$col'"
                    css="getFieldClass($row())" click="getFieldHandler($row())" template="getBody()"/>
            </tr>
            <tr ifnot="hasData()" class="data-grid-tr-no-data">
                <td attr="colspan: countVisible()" translate="'We couldn\'t find any records.'"/>
            </tr>
        </tbody>
    </table>
</div>

2. Repeat

You can use repeat binding alternative of foreach. This is also faster than foreach binding.

Here’s a comparison between foreach and repeat for a data table:

<table>
    <tbody data-bind="foreach: data">
        <tr data-bind="foreach: $parent.columns">
            <td data-bind="text: $parent[$data.propertyName]"></td>
        </tr>
    </tbody>
</table>

<table>
    <tbody>
        <tr data-bind="repeat: { foreach: data, item: '$row' }">
            <td data-bind="repeat: { foreach: columns, item: '$col' }"
                data-repeat-bind="text: $row()[$col().propertyName]"></td>
        </tr>
    </tbody>
</table>

For more details, you can check here

Magento uses this in timeline:
vendor/magento/module-ui/view/base/web/templates/timeline/timeline.html

How to create knockoutjs custom binding in Magento 2

Magento 2 uses knockoutjs for rendering UI component. Today I will discuss how you can use existing knockoutjs binding and how you can create a new binding.

What is binding?
Binding or a data binding is a way to link your ViewModels to your Views and vice versa.

What custom bindings are available in knockoutjs?
Magento 2 uses all default knockoutjs bindings and uses some custom bindings too. Most important binding is scope binding. You can read all custom bindings from devdocs. All custom bindings under /vendor/magento/module-ui/view/base/web/js/lib/knockout/bindings

How to use?

Check the scope binding example:

<div class="greet welcome" data-bind="scope: 'customer'">
    <!-- ko if: customer().fullname  -->
    <span class="logged-in" data-bind="text: customer().fullname"></span>
    <!-- /ko -->
</div>
<script type="text/x-magento-init">
{
    "*": {
        "Magento_Ui/js/core/app": {
            "components": {
                "customer": {
                    "component": "Magento_Customer/js/view/customer"
                }
            }
        }
    }
}
</script>

Here scope is customer. Where a component is important and this must me extend uiComponent(vendor/magento/module-ui/view/base/web/js/lib/core/element/element.js).

How to create custom bindings?
1. Create app/code/VendorName/ModuleName/view/frontend/web/js/custom-binding.js

define([
    'ko',
    'Magento_Ui/js/lib/knockout/template/renderer'
], function (ko, renderer) {
    'use strict';

    ko.bindingHandlers.customBinding = {

        init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
            // This will be called when the binding is first applied to an element
            // Set up any initial state, event handlers, etc. here
            element.innerHTML = 'Custom Binding';
        },
        update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
            // This will be called once when the binding is first applied to an element,
            // and again whenever any observables/computeds that are accessed change
            // Update the DOM element based on the supplied values here.
        }
    };

    renderer
        .addNode('customBinding')
        .addAttribute('customBinding');
});

2. Now you need to register. Create app/code/VendorName/ModuleName/view/frontend/requirejs-config.js

var config = {
    config: {
        mixins: {
            'Magento_Ui/js/lib/knockout/bindings/bootstrap': {
                'VendorName_ModuleName/js/lib/knockout/bindings/bootstrap': true
            }
        }
    }
};

3. Create app/code/VendorName/ModuleName/view/frontend/web/js/lib/knockout/bindings/bootstrap.js

define(function (require) {
        'use strict';
        return function (target) {
            target.customBinding = require('VendorName_ModuleName/js/custom-binding');
            return target;
        }
    }
);

Now you can able to use this custom binding. Ex:

<div class="greet welcome" data-bind="scope: 'custom-binding-example'">
    <span data-bind="customBinding:'true'"></span>
</div>
<script type="text/x-magento-init">
{
    "*": {
        "Magento_Ui/js/core/app": {
            "components": {
                "custom-binding-example": {
                    "component": "uiComponent"
                }
            }
        }
    }
}
</script>

Pass Magento 2 Certified Professional JavaScript Developer

I have passed the Magento 2 Certified Professional JavaScript Developer exam yesterday. Today I share my experience, preparation, etc with great Magento community.

When I took my Magento 2 Certified Professional JavaScript Developer exam?
I took my Magento 2 Certified Professional JavaScript Developer exam on 28th July 2019. It is a great feeling that I am now Magento 2 Certified Full-Stack Developer. I have passed 6 Magento 2 exams 😀

How I start My preparation?
Well, I set my mind to take this exam when Magento declared new Magento 2 Certified Full-Stack Developer exam. So I start learning all topic from study guide and practice. Also, checked my last project where I worked lots of modification using JS. When I worked with a project I didn’t touch many areas. One of is how Magento works with layout(uiLayout), that means core things. So I start learning from here. I already covered many areas when I take Professional Plus, Professional and Front-end exam. So it was my plus point that I just revised those areas. If you have working experience how ajax data handle using knockoutjs, how checkout work then you can easily answer all questions of those areas. No need to learn php stuff, I didn’t see any question related to that.

Who can take this exam?
Developer who have 2 years of hands-on working experience with Magento 2. It is important that you have must hands-on experience. If you think you memorized all area according to study guide then you should think twice before taking this exam. My opinion will be practice as much as you can.

How hard this exam?
Honestly, it was not a hard exam if you have working experience. All questions were logically and you can answer those question easily if you have working experience. I already said I learned most of the area when I took other Magento 2 exams, I think that helps me lots.

What is the passing score?

  • 60 multiple choice questions
  • 90 minutes to complete the exam
  • 63% or higher needed to pass

Where can I find a good resource?
Still, devdocs is a good resource that I have found. Also, check every topic from the study guide. Practice more on every topic.

Feel free to reach me if you have any question regarding certification. I am active in Magento Slack, Magento StackExchange or through my blog.

How to overwrite Swatch Renderer JS in Magento 2

In Magento 2, you can easily overwrite any js using mixin. So today I will show, how you can overwrite core Swatch Renderer Js.

Step 1: Create app/code/VendorName/ModuleName/view/frontend/requirejs-config.js

var config = {
    config: {
        mixins: {
            'Magento_Swatches/js/swatch-renderer': {
                'VendorName_ModuleName/js/swatch-renderer-mixin': true
            }
        }
    }
};

Step 2: Create app/code/VendorName/ModuleName/view/frontend/web/js/swatch-renderer-mixin.js

define(['jquery'], function ($) {
    'use strict';

    return function (SwatchRenderer) {
        $.widget('mage.SwatchRenderer', $['mage']['SwatchRenderer'], {
            _init: function () {
                console.log('getProductSwatchRenderer');
                this._super();
            }
        });
        return $['mage']['SwatchRenderer'];
    };
});

That’s it.

Now you need to remove static content and deploy static content.

How to get customer id from block when full page cache enable in magento 2

Today I discuss about getting customer_id from block when full page cache(FPC) is enable. If FPC is enable then you can’t get customer id from block just because magento2 reset all customer session. You can get customer session data if your layout set cacheable=”false” in that case magento2 just ignore cache whole page. How? lets dive.

If you open vendor/magento/module-customer/Model/Layout/DepersonalizePlugin.php


/**
 * After generate Xml
 *
 * @param \Magento\Framework\View\LayoutInterface $subject
 * @param \Magento\Framework\View\LayoutInterface $result
 * @return \Magento\Framework\View\LayoutInterface
 */
public function afterGenerateXml(\Magento\Framework\View\LayoutInterface $subject, $result)
{
    if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) {
        $this->visitor->setSkipRequestLogging(true);
        $this->visitor->unsetData();
        $this->session->clearStorage();
        $this->customerSession->clearStorage();
        $this->session->setData(\Magento\Framework\Data\Form\FormKey::FORM_KEY, $this->formKey);
        $this->customerSession->setCustomerGroupId($this->customerGroupId);
        $this->customerSession->setCustomer($this->customerFactory->create()->setGroupId($this->customerGroupId));
    }
    return $result;
}

Now check the if condition, basically this return true/false from following function
vendor/magento/module-page-cache/Model/DepersonalizeChecker.php


/**
 * Check if depersonalize or not
 *
 * @param \Magento\Framework\View\LayoutInterface $subject
 * @return bool
 * @api
 */
public function checkIfDepersonalize(\Magento\Framework\View\LayoutInterface $subject)
{
    return ($this->moduleManager->isEnabled('Magento_PageCache')
        && $this->cacheConfig->isEnabled()
        && !$this->request->isAjax()
        && ($this->request->isGet() || $this->request->isHead())
        && $subject->isCacheable());
}

Here $subject->isCacheable() return false if your layout has ‘cacheable=”false”‘ attribute, lets check
vendor/magento/framework/View/Layout.php


/**
 * Check is exists non-cacheable layout elements
 *
 * @return bool
 */
public function isCacheable()
{
    $this->build();
    $cacheableXml = !(bool)count($this->getXml()->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]'));
    return $this->cacheable && $cacheableXml;
}

So if full page cahce enable and cacheable=”false” is not exist then magento 2 reset customer session by


$this->session->clearStorage();
$this->customerSession->clearStorage();

That’s why if you try to get customer session data from block, it’s return always empty basically only group_id is present in customer session data.

So how to get customer information then.

You can follow Magento2 official tutorial.

Now Today I discuss how you can get customer_id if cache enable. Lets dive.

Suppose vendor name ‘SR‘ and module name ‘CustomerSession

Create a contex class [SR/CustomerSession/Model/Customer/Context.php]


namespace SR\CustomerSession\Model\Customer;

class Context
{
    /**
     * Customer authorization cache context
     */
    const CONTEXT_CUSTOMER_ID = 'logged_in_customer_id';
}

Now pluginize Magento\Framework\App\Action\AbstractAction dispatch method.
So create a di.xml [SR/CustomerSession/etc/frontend/di.xml]


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\App\Action\AbstractAction">
        <plugin name="sr-customer-app-action-dispatchController-context-plugin" type="SR\CustomerSession\Plugin\App\Action\Context" sortOrder="15"/>
    </type>
</config>

Now create SR/CustomerSession/Plugin/App/Action/Context.php


namespace SR\CustomerSession\Plugin\App\Action;

use SR\CustomerSession\Model\Customer\Context as CustomerSessionContext;

class Context
{
    /**
     * @var \Magento\Customer\Model\Session
     */
    protected $customerSession;

    /**
     * @var \Magento\Framework\App\Http\Context
     */
    protected $httpContext;

    /**
     * @param \Magento\Customer\Model\Session $customerSession
     * @param \Magento\Framework\App\Http\Context $httpContext
     */
    public function __construct(
        \Magento\Customer\Model\Session $customerSession,
        \Magento\Framework\App\Http\Context $httpContext
    ) {
        $this->customerSession = $customerSession;
        $this->httpContext = $httpContext;
    }

    /**
     * @param \Magento\Framework\App\ActionInterface $subject
     * @param callable $proceed
     * @param \Magento\Framework\App\RequestInterface $request
     * @return mixed
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function aroundDispatch(
        \Magento\Framework\App\ActionInterface $subject,
        \Closure $proceed,
        \Magento\Framework\App\RequestInterface $request
    ) {
        $customerId = $this->customerSession->getCustomerId();
        if(!$customerId) {
            $customerId = 0;
        }

        $this->httpContext->setValue(
            CustomerSessionContext::CONTEXT_CUSTOMER_ID,
            $customerId,
            false
        );

        return $proceed($request);
    }
}

That’s it. Now you can able to get customer id following way even cache enable:


/**
* @var \Magento\Framework\App\Http\Context
*/
$this->httpContext->getValue(SR\CustomerSession\Model\Customer\Context::CONTEXT_CUSTOMER_ID);

Download full module from here

How to create a custom form in Magento2 admin

Magento2 have excellent feature which allow a developer to create the admin forms without coding any HTML. It is easy to create any form with any data. This article will guide you how to create such form.

If You miss my previous tutorial, take a overview from here. This tutorial is fully dependent on previous one. So read carefully.

Get a copy of this module? Download from here

Happy Coding!

Creating magento2 adminhtml grid

Magento2 provides an infrastructure, Adminhtml Grid, for you to do the admin backend development in an elegant way. In this article, I’ll introduce you the mechanism of the Adminhtml Grid and you’ll know how to manage the data through it easily and efficiently.

If you miss my previous magento2 related article, take a look from here.

Suppose Namespace : SR and Module : Weblog

Get a copy of this module? Download from here