Category: M2 Certified Professional Javascript Developer

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.