Magento 2: Co to jest tag `<each />`?

13

O ile wiem, kiedy oglądasz siatkę w backendzie Magento, następujący szablon KnockoutJS „załadowany przez XHR” zaczyna renderować rzeczy

File: vendor/magento//module-ui/view/base/web/templates/collection.html
URL:  http://magento.example.xom/pub/static/adminhtml/Magento/backend/en_US/Magento_Ui/templates/collection.html
<each args="data: elems, as: 'element'">
    <render if="hasTemplate()"/>
</each>

Jednak - jestem trochę zagubiony, co do <each/>tagu i <render/>tagu. Nie są (lub nie wydają się być) częścią akcji KnockoutJS.

Wiem, że można dodawać niestandardowe tagi do KnockoutJS za pośrednictwem komponentów , ale nie widzę żadnych oczywistych miejsc, w których komponent nazwany eachlub renderzostałby dodany do KnockoutJS.

Nie jestem więc pewien, czy są to komponenty zarejestrowane gdzieś, o których nie wiem, czy jakieś inne dostosowania dokonane przez Magento w KnockoutJS, które umożliwiają niestandardowe tagi, czy coś zupełnie innego.

Uwaga: Nie jestem tutaj całkowicie ukryty - rozumiem, że <each/>prawdopodobnie iteruje każdy komponent interfejsu użytkownika renderowany w JSON i wyświetla jego szablon (jeśli ten szablon istnieje).

Nie jestem wcale pewien, w jaki sposób te tagi są implementowane. Chcę zobaczyć, gdzie są zaimplementowane, aby móc debugować sposób wiązania danych, a także zrozumieć mechanizm, którego Magento używa do tworzenia tych tagów w przypadku, gdy istnieją inne.

Alan Storm
źródło

Odpowiedzi:

10

Jak zasugerował Raphael , okazuje się, że kiedy Magento pobiera szablony KnockoutJS za pośrednictwem żądania XHR (tj. Ajax), przekazuje je również przez niektóre niestandardowe procedury analizy, które szukają wielu niestandardowych tagów i atrybutów

To niestandardowe parsowanie jest wykonywane przez Magento_Ui/js/lib/knockout/template/renderermoduł RequireJS. Kod źródłowy tego modułu ustawia szereg domyślnych znaczników i atrybutów do wyszukiwania. Istnieją również inne moduły, które mogą dodawać dodatkowe znaczniki i atrybuty do tego mechanizmu renderującego. Na przykład następujące

#File: vendor/magento/module-ui/view/base/web/js/lib/knockout/bindings/scope.js
renderer
    .addNode('scope')
    .addAttribute('scope', {
        name: 'ko-scope'
    });

doda <scope/>tag i scopeatrybut ( <div scope="...">) do listy możliwych do przeanalizowania atrybutów.

Czy wydaje się podstawowa idea jest przełożenie tych znaczników i atrybutów w rodzimych Knockout „Tagless” bloków szablonów. Na przykład następujący szablon KnockoutJS Magento

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

Przekłada się na następujący natywny kod KnockoutJS

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

Dokładne zasady tego tłumaczenia są dla mnie nadal niejasne - kod w nim Magento_Ui/js/lib/knockout/template/rendererjest trochę pośredni i wygląda na to, że mogą zmieniać się między tagami, atrybutami do atrybutów.

Zebrałem następujący fragment kodu, który może pobrać szablon KnockoutJS Magento i przetłumaczyć go na natywny kod KnockoutJS.

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);    
})

Co do tego, dlaczego Magento może to zrobić - domyślam się, że chcę jakiegoś rodzaju podświetlania składni i czytelności szablonu komentowania KnockoutJS, ale nigdy nie wykluczam więcej powodów związanych z Mallory .

Alan Storm
źródło
2

Oba tagi są zaimplementowane w ramach app/code/Magento/Ui/view/base/web/js/lib/knockout/template/renderer.js, ale nie jestem pewien, czy dokładnie rozumiem, jak są zaimplementowane:

_.extend(preset.nodes, {
    foreach: {
        name: 'each'
    },

    /**
     * Custom 'render' node handler function.
     * Replaces node with knockout's 'ko template:' comment tag.
     *
     * @param {HTMLElement} node - Element to be processed.
     * @param {String} data - Data specified in 'args' attribute of a node.
     */
    render: function (node, data) {
        data = data || 'getTemplate()';
        data = renderer.wrapArgs(data);

        renderer.wrapNode(node, 'template', data);
        $(node).replaceWith(node.childNodes);
    }
});
Raphael at Digital Pianism
źródło