Categories


Archives


Recent Posts


Categories


Magento’s KnockoutJS Templates aren’t KnockoutJS Templates

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

I’ve been digging through Magento’s UI Components this week and hit a stumbling block. The main KnockoutJS template that kicks off rendering (vendor/magento//module-ui/view/base/web/templates/collection.html) looks like this

<each args="data: elems, as: 'element'"><render if="hasTemplate()"></render></each>

This sort of thing is exactly where Magento hits the unsweet spot of being new, and using a technology I’m not super familiar with (KnockoutJS).

I’m pretty sure the HTML5 kids haven’t added there’s an <each></each> or <render></render> tag to HTML.

In my readings through KnockoutJS’s docs I discover that component bindings create custom tags, but some unfruitful searching through Magento’s code doesn’t show any obvious place where this has been done for an each or render tag. Some more digging into KnockoutJS’s implementation let me rule this out for sure by checking the registered binding handlers from a javascript console

ko = require('ko')
console.log(ko.bindingHandlers);

This leads me back to tracing out how Magento loads its KnockoutJS template over XHR, and I discover Magento’s also added some customizations that add custom tags and attributes to these templates. The short version – Magento’s build its own macro layer on top of KnockoutJS’s templates.

The rules of this system are still unclear to me, but I’ve ginned up a quick Javascript snippet you can run from your browser’s debugging console (on a bootstrapped Magento page) that will translate one of Magento’s KnockoutJS templates (loaded from a URL) into its native KnockoutJS equivalent.

jQuery.get('http://magento-2-1-0.dev/static/adminhtml/Magento/backend/en_US/Magento_Ui/templates/collection.html', function(result){
    var renderer = requirejs('Magento_Ui/js/lib/knockout/template/renderer')
    var fragment = document.createDocumentFragment();
    $(fragment).append(result);

    //fragment is passed by reference, modified
    renderer.normalize(fragment);
    var string = new XMLSerializer().serializeToString(fragment);
    console.log(string);    
})

Run this with collection.html in your system, and you’ll get a more KnockoutJS looking template

<!-- ko foreach: {data: elems, as: 'element'} -->
    <!-- ko if: hasTemplate() --><!-- ko template: getTemplate() --><!-- /ko --><!-- /ko -->
<!-- /ko -->

I don’t see a lot of documentation on this at the moment, but I’ve got an open ticket with dev docs that might shake some information loose from the tree.

Copyright © Alan Storm 1975 – 2017 All Rights Reserved

Originally Posted: 7th July 2016