1

I was studying about Knockout.js and how it works with Magento 2.

Then I reached the topic: template integration, in the first sample of it I'm presented to this notation:

<div data-bind="template:'Namespace_Module/sample'"></div>

Which will try to render the template located at:

app/code/Namespace/Module/view/frontend/web/template/sample.html

It's working in Magento versions equals/lower than 2.1.

In greater versions it's throwing the following error:

Uncaught TypeError: Unable to process binding "template: function(){return 'Namespace_Module/sample' }"
Message: Cannot read property 'name' of undefined
    at RemoteTemplateEngine.makeTemplateSource (engine.js:55)
    at RemoteTemplateEngine.renderTemplate (engine.js:116)
    at executeTemplate (knockout.js:4886)
    at ko.dependentObservable.disposeWhen (knockout.js:4950)
    at evaluateImmediate (knockout.js:1737)
    at Object.ko.computed.ko.dependentObservable (knockout.js:1946)
    at Object.ko.renderTemplate (knockout.js:4942)
    at update (knockout.js:5080)
    at ko.dependentObservable.disposeWhenNodeIsRemoved (knockout.js:3004)
    at evaluateImmediate (knockout.js:1737)

I was trying to understand why it's happening looking for the change in github, but it's not clear to me:

https://github.com/magento/magento2/commit/d99043c097816388b2aeec1c23e10bf15e064b28#diff-9ec27f11f8253078a99c9d1e979a07f3

Now it requires a bindingContext (which contain the name attribute requested in exception) but I don't know how it's initialized.

3 Answers 3

0

It appears a UI Component is now required for KnockoutJS component initialization, due to changes in their custom renderer.

There's a lot of boiler to set this up, but you would:

  • Add a jsLayout argument to your layout block, defining the name of your component and KnockoutJS template

app/code/Macademy/KoTest/view/frontend/layout/kotest_index_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block name="ko.test" template="Macademy_KoTest::ko-test.phtml">
                <arguments>
                    <argument name="jsLayout" xsi:type="array">
                        <item name="components" xsi:type="array">
                            <item name="ko-test" xsi:type="array">
                                <item name="component" xsi:type="string">Macademy_KoTest/js/ko-test</item>
                                <item name="config" xsi:type="array">
                                    <item name="template" xsi:type="string">Macademy_KoTest/my-knockout-template</item>
                                </item>
                            </item>
                        </item>
                    </argument>
                </arguments>
            </block>
        </referenceContainer>
    </body>
</page>
  • Create your UI component

app/code/Macademy/KoTest/view/frontend/web/js/ko-test.js

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

    console.log('ko-test UI Component has been loaded');


    // You'd normally always return Component.extend(), then pass the
    // configuration as JSON into this parent class.

    return Component;
});
  • Then create your PHTML file, bound to the scope of your UI Component, which can then display the template defined in your XML (or hardcoded, etc)

app/code/Macademy/KoTest/view/frontend/templates/ko-test.phtml

<?php /* @var $block Magento\Framework\View\Element\Template */ ?>

<div id="ko-test" data-bind="scope: 'ko-test'">
    <!-- ko template: getTemplate() --><!-- /ko -->
</div>

<script type="text/x-magento-init">
    {
        "#ko-test": {
            "Magento_Ui/js/core/app": <?= $block->getJsLayout() ?>
        }
    }
</script>

Note that you just wanted to display the Knockout template, and nothing else, you can replace Macademy_KoTest/js/ko-test with uiComponent in the XML file and not even have to create a custom UI component file, but usually you are doing more than just displaying a template file.

The instantiation is a bit different from KnockoutJS's, as the default looks for rendered templates on the page, while Magento's looks for file paths https://devdocs.magento.com/guides/v2.3/ui_comp_guide/concepts/knockout-bindings.html#template

1

I just came across this same exact issue while following Alan Storm's KnockoutJs integration article.

I saw the following on another one of his articles (https://alanstorm.com/knockoutjs_primer_for_magento_developers/):

<div id="one" data-bind="template:{'name':'hello-world','data':first}"></div>

I haven't gotten it working yet, but perhaps this is the way to initialize the name attribute? Hopefully someone with more knowledge of magento2 can chime in with a better answer.

0

I just came across this same issue and solved it by this way..

Change your content.phtml like this

<div id="custom-component" data-bind="scope:'customcomponent'">
<script type="text/x-magento-init">
   custom-component": {
      "Magento_Ui/js/core/app": {
          "components": {
             "customcomponent": {
                "component": "Namespace_Module/js/custom-component"
                                }
                            }
                         }
                    }
                }
        </script>
     </div>

Create custome component in Namespace/module/view/frontend/web/js/custom-component.js with code

define(['jquery', 'uiComponent', 'ko'], function ($, Component, ko) {
         'use strict';
    return Component.extend({
        defaults: {
            template: 'Namespace_module/hello'
        },
        initialize: function () {
            this._super();
        }
    });
}

);

clear cache and reload your page it starts working..Hope it helps any new learne

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.