Magento 2:「getTemplate」ノックアウト関数はどのように/どこにバインドされますか?


19

多くのMagentoバックエンドページのソースコードには以下が含まれています

<!-- ko template: getTemplate() --><!-- /ko -->

私はそれを理解しています(またはそう思いますか?)、これ<!-- ko templateはKnockoutJSのコンテナーなしのテンプレートバインディングです。

私には明らかでないのは、getTemplate()関数が呼び出されるコンテキストは何ですか?私がオンラインで見た例では、通常、の後にjavascriptオブジェクトがありtemplate:ます。私はそれgetTemplateがオブジェクトを返すjavascript関数であると仮定しています-しかし、という名前のグローバル javascript関数はありませんgetTemplate

どこにgetTemplate縛られていますか?または、より良い質問かもしれませんが、KnockoutJSアプリケーションバインディングはMagentoバックエンドページのどこで発生しますか?

純粋なHTML / CSS / Javascriptの観点からこれに興味があります。Magento 2には多くの構成の抽象化があるため、(理論上)開発者は実装の詳細について心配する必要はありません。実装の詳細に興味があります。

回答:


38

UIコンポーネントのPHPコードは、次のようなjavascriptの初期化をレンダリングします

<script type="text/x-magento-init">
    {
        "*": {
            "Magento_Ui/js/core/app":{
                "types":{...},
                "components":{...},
            }
        }
    }
</script>       

このページのコードは、MagentoがMagento_Ui/js/core/appRequireJSモジュールを呼び出してコールバックを取得し、そのコールバックを呼び出して{types:..., components:...}JSONオブジェクトを引数として渡すことを意味します(data以下)

#File: vendor/magento/module-ui/view/base/web/js/core/app.js
define([
    './renderer/types',
    './renderer/layout',
    'Magento_Ui/js/lib/ko/initialize'
], function (types, layout) {
    'use strict';

    return function (data) {
        types.set(data.types);
        layout(data.components);
    };
});

データオブジェクトには、UIコンポーネントのレンダリングに必要なすべてのデータと、特定の文字列を特定のMagento RequireJSモジュールにリンクする構成が含まれています。そのマッピングはRequireJSモジュールtypeslayout行われます。アプリケーションは、Magento_Ui/js/lib/ko/initializeRequireJSライブラリもロードします。このinitializeモジュールは、MagentoのKnockoutJS統合を開始します。

/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
/** Loads all available knockout bindings, sets custom template engine, initializes knockout on page */

#File: vendor/magento/module-ui/view/base/web/js/lib/ko/initialize.js
define([
    'ko',
    './template/engine',
    'knockoutjs/knockout-repeat',
    'knockoutjs/knockout-fast-foreach',
    'knockoutjs/knockout-es5',
    './bind/scope',
    './bind/staticChecked',
    './bind/datepicker',
    './bind/outer_click',
    './bind/keyboard',
    './bind/optgroup',
    './bind/fadeVisible',
    './bind/mage-init',
    './bind/after-render',
    './bind/i18n',
    './bind/collapsible',
    './bind/autoselect',
    './extender/observable_array',
    './extender/bound-nodes'
], function (ko, templateEngine) {
    'use strict';

    ko.setTemplateEngine(templateEngine);
    ko.applyBindings();
});

個々のbind/...RequireJSモジュールは、Knockoutの単一のカスタムバインディングをセットアップします。

extender/...RequireJSモジュールは、ネイティブKnockoutJSオブジェクトにいくつかのヘルパーメソッドを追加します。

Magentoは、./template/engineRequireJSモジュールのKnockoutのjavascriptテンプレートエンジンの機能も拡張します。

最後に、MagentoはapplyBindings()KnockoutJSオブジェクトを呼び出します。これは通常、KnockoutプログラムがビューモデルをHTMLページにバインドする場所ですが、MagentoはビューモデルapplyBindings なしで呼び出します。これは、Knockoutがページをビューとして処理し始めますが、データがバインドされていないことを意味します。

ストックノックアウトのセットアップでは、これは少しばかげているでしょう。ただし、前述のカスタムKnockoutバインディングのため、Knockoutが処理を実行する機会はたくさんあります。

スコープバインディングに興味があります。このHTMLでも、PHP UIコンポーネントシステムによってレンダリングされていることがわかります。

<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
    <div data-role="spinner" data-component="customer_listing.customer_listing.customer_columns" class="admin__data-grid-loading-mask">
        <div class="spinner">
            <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
        </div>
    </div>
    <!-- ko template: getTemplate() --><!-- /ko -->
    <script type="text/x-magento-init">
    </script>
</div>

具体的には、data-bind="scope: 'customer_listing.customer_listing'">属性。Magentoが開始されるとapplyBindings、Knockoutはこのカスタムscopeバインディングを確認し、./bind/scopeRequireJSモジュールを呼び出します。カスタムバインディングを適用する機能は、純粋なKnockoutJSです。スコープバインディングの実装は、Magento Inc.が行ったことです。

スコープバインディングの実装は

#File: vendor/magento/module-ui/view/base/web/js/lib/ko/bind/scope.js

このファイルの重要な部分はこちらです

var component = valueAccessor(),
    apply = applyComponents.bind(this, el, bindingContext);

if (typeof component === 'string') {
    registry.get(component, apply);
} else if (typeof component === 'function') {
    component(apply);
}

詳細に入ることなく、registry.getメソッドはcomponent変数内の文字列を識別子として使用して既に生成されたオブジェクトを引き出しapplyComponents、3番目のパラメーターとしてメソッドに渡します。文字列識別子はscope:customer_listing.customer_listing上記)の値です

applyComponents

function applyComponents(el, bindingContext, component) {
    component = bindingContext.createChildContext(component);

    ko.utils.extend(component, {
        $t: i18n
    });

    ko.utils.arrayForEach(el.childNodes, ko.cleanNode);

    ko.applyBindingsToDescendants(component, el);
}

の呼び出しcreateChildContextは、基本的に、既にインスタンス化されたコンポーネントオブジェクトに基づいて新しいviewModelオブジェクトを作成し、それをdiv使用した元のすべての子孫要素に適用しますdata-bind=scope:

それで、すでにインスタンス化されたコンポーネントオブジェクトは何ですか?layout戻るための呼び出しを覚えていますapp.jsか?

#File: vendor/magento/module-ui/view/base/web/js/core/app.js

layout(data.components);

layout機能/モジュールは、渡さに下降しますdata.components(再度、このデータを介して渡されたオブジェクトから来ていますtext/x-magento-init)。見つかったオブジェクトごとにオブジェクトを探し、その構成オブジェクトでキーconfigを探しcomponentます。コンポーネントキーが見つかると、

  1. / 依存関係RequireJSでモジュールが呼び出されたかのように、モジュールインスタンスを返すために使用します。requirejsdefine

  2. そのモジュールインスタンスをjavascriptコンストラクタとして呼び出す

  3. 結果のオブジェクトをregistryオブジェクト/モジュールに保存します

だから、それは多くのことを取り入れる必要があります。

<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
    <div data-role="spinner" data-component="customer_listing.customer_listing.customer_columns" class="admin__data-grid-loading-mask">
        <div class="spinner">
            <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
        </div>
    </div>
    <!-- ko template: getTemplate() --><!-- /ko -->
    <script type="text/x-magento-init">
    </script>
</div>

出発点として。scope値ですcustomer_listing.customer_listing

text/x-magento-init初期化からJSONオブジェクトを見ると

{
    "*": {
        "Magento_Ui/js/core/app": {
            /* snip */
            "components": {
                "customer_listing": {
                    "children": {
                        "customer_listing": {
                            "type": "customer_listing",
                            "name": "customer_listing",
                            "children": /* snip */
                            "config": {
                                "component": "uiComponent"
                            }
                        },
                        /* snip */
                    }
                }
            }
        }
    }
}

私たちは、参照components.customer_listing.customer_listingオブジェクトが持っているconfigオブジェクトを、そのconfigオブジェクトが持っているcomponentに設定されていますオブジェクトをuiComponentuiComponent文字列は、RequireJSモジュールです。実際、そのMagento_Ui/js/lib/core/collectionモジュールに対応するRequireJSエイリアスです。

vendor/magento/module-ui/view/base/requirejs-config.js
14:            uiComponent:    'Magento_Ui/js/lib/core/collection',

ではlayout.js、Magentoは次と同等のコードを実行しました。

//The actual code is a bit more complicated because it
//involves jQuery's promises. This is already a complicated 
//enough explanation without heading down that path

require(['Magento_Ui/js/lib/core/collection'], function (collection) {    
    object = new collection({/*data from x-magento-init*/})
}

好奇心For盛な人のために、コレクションモデルを見て、その実行パスをたどると、モジュールとモジュールのcollection両方によって拡張されたjavascriptオブジェクトであることがわかります。これらのカスタマイズの調査は、この回答の範囲外です。lib/core/element/elementlib/core/class

インスタンス化したら、layout.jsこれobjectをレジストリに保存します。これは、ノックアウトは、バインディングの処理を開始し、カスタム遭遇したscope結合を

<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
    <!-- snip -->
    <!-- ko template: getTemplate() --><!-- /ko -->
    <!-- snip -->
</div>

Magentoはこのオブジェクトをレジストリからフェッチし、内のオブジェクトのビューモデルとしてバインドしますdiv。つまり、getTemplateKnockoutがタグレスバインディング(<!-- ko template: getTemplate() --><!-- /ko -->)を呼び出すときに呼び出されるgetTemplateメソッドは、new collectionオブジェクトのメソッドです。


1
私はあなたの答えに「なぜ」という質問をするのが嫌いなので、より焦点を絞った質問は、この(一見複雑な)システムを使用してKOテンプレートを呼び出すことでM2は何を得るのでしょうか?
-circlesix

1
@circlesix <uiComponents/>レイアウトXMLシステムからレンダリングするためのより大きなシステムの一部。彼らが得られるメリットは、同じページのビューモデルを異なるタグセットに交換できることです。
アランストーム

16
笑うのか泣くのかわからない!なんて混乱だ。
koosa

8
彼らは自分の墓を掘っていると思います。彼らはこのようなものを複雑に保つ場合は、企業があるため、開発費の使用を中止します
マリアンジークŠedaj

2
私は、このすべての「魔法」によってレンダリングされるフォームにカスタム動作をバインドする方法を見つけるのに5時間ほど費やしています。問題の一部は、この非常に汎用的なフレームワークでは、物事の方法を理解する機会が得られるまで、大量のレイヤーを通過する必要があるということです。また、特定の構成がどこから来ているかを追跡することは、非常に面倒です。
greenone83

12

ノックアウトJSテンプレートのバインディングは、モジュールの.xmlファイルで行われます。例としてCheckoutモジュールを使用すると、contentテンプレートの構成を見つけることができますvendor/magento/module-checkout/view/frontend/layout/default.xml

<block class="Magento\Checkout\Block\Cart\Sidebar" name="minicart" as="minicart" after="logo" template="cart/minicart.phtml">
    <arguments>
        <argument name="jsLayout" xsi:type="array">
            <item name="types" xsi:type="array"/>
                <item name="components" xsi:type="array">
                    <item name="minicart_content" xsi:type="array">
                        <item name="component" xsi:type="string">Magento_Checkout/js/view/minicart</item>
                            <item name="config" xsi:type="array">
                                <item name="template" xsi:type="string">Magento_Checkout/minicart/content</item>
                            </item>

このファイルでは、ブロッククラスに「jsLayout」を定義し、を呼び出すノードがあることがわかります<item name="minicart_content" xsi:type="array">。それは論理のラウンドロビンの少しですが、vendor/magento/module-checkout/view/frontend/templates/cart/minicart.phtmlあなたがいる場合は、この行が表示されます:

<div id="minicart-content-wrapper" data-bind="scope: 'minicart_content'">
    <!-- ko template: getTemplate() --><!-- /ko -->
</div>

だから、データバインドところ、それはだ、この場合には、ネストされたテンプレートを探すように指示するMagento_Checkout/js/view/minicartvendor/magento/module-checkout/view/frontend/web/js/view/minicart.jsロジックのために(またはMVノックアウトモデル-ビュー-ビューモデル系で)、あなたが持っているMagento_Checkout/minicart/content(またはノックアウトモデル-ビュー-ビューモデルでVシステム)テンプレート呼び出し用。したがって、このスポットでプルされるテンプレートはvendor/magento/module-checkout/view/frontend/web/template/minicart/content.htmlです。

.xmlの検索に慣れたら、理解するのは難しくありません。これの大部分は、ここで、壊れた英語を乗り越えられるかどうかを学びました。しかし、これまでのところ、Knockout統合はM2のドキュメント化されていない部分のようです。


2
有用な情報、つまり+1 つまり、そのXMLファイルで何かを構成するとき、magentoは構成された値が3番目のことを行うことを確認するために何か他のことを行います。私は他の何かと3番目のことに興味があります。
アランストーム

4

私はかなり確信している世界的な getTemplateあなたが探しているJSのメソッドが下に定義されapp/code/Magento/Ui/view/base/web/js/lib/core/element/element.jsます、それをここで見つけることができます。https://github.com/magento/magento2/blob/4d71bb4780625dce23274c90e45788a72f345dd9/app/code/Magento/Ui/view/base /web/js/lib/core/element/element.js#L262

私は自分の携帯電話を使っているので、バインディングがどのように行われているかを正確に見つけるのに苦労しています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.