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.