Как использовать Knockout JS в Magento 2

12

Моя проблема:

Я пытаюсь написать небольшое приложение Knockout JS в Magento 2, я изо всех сил пытаюсь инициализировать приложение, так как, когда я ko.applyBindings(AppViewModel, document.getElementById("koTest"));его использую, он ломает Knockout, используемый Magento, и выдает эту ошибку:

Uncaught Error: You cannot apply bindings multiple times to the same element.

Я подозреваю, что это из-за:

Я подозреваю, что это потому, что Magento 2 уже использует ko.applyBindings()внутри app/code/Magento/Ui/view/base/web/js/lib/knockout/bootstrap.js. И поскольку это не определяет узел, я не могу использовать ko.applyBindingsснова.

Если я не использую ko.applyBindings(AppViewModel, document.getElementById("koTest"))в своем коде, то мое приложение не инициализируется.

Это заставляет меня думать, что мне нужно каким-то образом использовать ko.applyBindings()in knockout / bootstrap.js, но я не знаю, как, кто-нибудь может помочь? У меня мало опыта с нокаутом.

Мой код

<script type="text/javascript">
    require([
        'ko'
    ], function(ko) {
        // This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
        function AppViewModel() {

            this.firstName = ko.observable("Bert");
            this.lastName = ko.observable("Bertington");
            this.fullName = ko.computed(function() {
                return this.firstName() + " " + this.lastName();
            }, this);

            this.capitalizeLastName = function() {
                var currentVal = this.lastName();
                this.lastName(currentVal.toUpperCase());
            };
        }

        ko.applyBindings(AppViewModel, document.getElementById("koTest"));
    });
</script>

<!-- This is a *view* - HTML markup that defines the appearance of your UI -->

<div id="koTest">
    <p>First name: <strong data-bind="text: firstName"></strong></p>
    <p>Last name: <strong data-bind="text: lastName"></strong></p>
    <p>Full name: <strong data-bind="text: fullName"></strong></p>

    <p>First name: <input data-bind="value: firstName" /></p>
    <p>Last name: <input data-bind="value: lastName" /></p>
    <p>Full name: <input data-bind="value: fullName" /></p>

    <button data-bind="click: capitalizeLastName">Capitalise</button>
</div>
Бен Крук
источник
1
Здесь есть учебное пособие: inchoo.net/magento-2/knockout-js-in-magento-2
Аарон Аллен

Ответы:

23

Простой метод, где вам не нужно использовать HTML-шаблоны

Благодаря Vinai Kopp я наконец получил ответ на этот вопрос, он намного проще, чем мой предыдущий хакерский обходной путь (я чистил узлы). Все, что вам нужно сделать, это определить 'ko'как зависимость и добавить свой код в функцию возврата.

Ниже приведен простой пример, который отображает некоторый текст, переданный через JSON.

app/code/VENODR/MODULE/view/frontend/templates/knockout-example.phtml

Здесь мы сообщаем Magento область действия наших компонентов (это должно соответствовать data-bind: "scope: 'example-scope'"и передавать любые дополнительные данные. Это может быть базовый URL, простое сообщение, почти все, что вы хотите. Я передал строку (PHP echo) в качестве примера

<script type="text/x-magento-init">
{
    "*": {
        "Magento_Ui/js/core/app": {
            "components": {
                "example-scope": {
                    "component": "VENDOR_MODULE/js/knockout-example",
                    "exampleMessage": "<?= __('Hello Magento Stack Exchange!') ?>"
                }
            }
        }
    }
}
</script>

<div data-bind="scope: 'example-scope'">
    <h2 data-bind="text: message"></h2>
</div>

И вот мы пишем наш Javascript.

app/code/VENDOR/MODULE/view/frontend/web/js/knockout-example.js

define(['ko'], function(ko) {
    return function(config) {
        this.message = ko.observable(config.exampleMessage);
    }
});

 Результат

введите описание изображения здесь

---------------------

Метод, в котором вам нужно использовать шаблоны HTML

Если вы хотите использовать систему шаблонов HTML в Magento2 / Knockout (которая, как я полагаю, вам понадобится для каких-либо значительных частей работы), вам нужно внести несколько изменений по сравнению с моим упрощенным ответом (ниже).

Если вам не нужна функциональность шаблона, прокрутите вниз до моего старого упрощенного ответа.

Файлы, которые я использую для этого примера:

  • app/design/frontend/VENDOR/THEME/Magento_Cms/templates/knockout.phtml
  • app/design/frontend/VENDOR/THEME/Magento_Cms/web/js/knockout-example.js
  • app/design/frontend/VENDOR/THEME/Magento_Cms/web/template/test.html

Файл шаблона PHTML

Единственное изменение в нашем шаблоне PHTML - это вызов getTemplate()функции:

<script type="text/x-magento-init">
{
    "*": {
        "Magento_Ui/js/core/app": {
            "components": {
                "example-scope": {
                    "component": "Magento_Cms/js/knockout-example",
                    "exampleMessage": "<?= __('Hello Magento Stack Exchange!') ?>"
                }
            }
        }
    }
}
</script>

<div data-bind="scope: 'example-scope'">
    <h2 data-bind="text: message"></h2>
    <!-- ko template: getTemplate() --><!-- /ko -->
</div>

Файл JS (компонент)

Есть несколько изменений, которые необходимо внести в файл JS, я подробно опишу их ниже.

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

    return Component.extend({
        defaults: {
            exampleMessage: 'Hello?',
            template: 'Magento_Cms/test'
        },

        initialize: function() {
            this._super();
            console.log(this.exampleMessage);
            this.message = ko.observable(this.exampleMessage);
        }
    });
});

1 - Ваша функция возврата теперь должна расширять модуль uiComponent:

return Component.extend({
    ...
});

2 - Вам нужно добавить initializeфункцию и вызвать this._super(). this._super()вызовет функцию родительского компонента с тем же именем. Так что в данном случае я думаю , что он будет вызывать initializeиз uiComponent.

initialize: function() {
    this._super();
    ...
}.

3 - Необязательно - Здесь вы также можете установить некоторые значения по умолчанию для вашего компонента, я думаю, что это хорошая практика, которой нужно следовать, поскольку с вашим компонентом легко работать. При повторном использовании вы можете сохранить значения по умолчанию или, если вы хотите изменить его, вы можете вызвать его с новыми аргументами без изменения компонента.

Например, если вы смотрите на значения по умолчанию в JS она устанавливает exampleMessageна 'Hello?'еще страница делает текст как Hello Magento Stack Exchange!. Это потому, что я перезаписал exampleMessageфайл PHTML при вызове компонента.

HTML-шаблон

Я все еще копаюсь и смотрю, на что способны HTML-шаблоны, я предполагаю, что здесь можно использовать функции, упомянутые в документации по Knockout JS, что делает их довольно гибкими.

Я только что добавил текст lorem ipsum, я, скорее всего, предоставлю еще один вопрос / ответ, как только выясню, что могут делать шаблоны HTML.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores assumenda beatae blanditiis culpa cupiditate doloremque, expedita ipsum iure magni minima modi molestiae nulla optio porro ratione reiciendis repellat soluta voluptatum!

Результат и перезапись по умолчанию

Как упоминалось ранее, вы можете видеть, что я переписал exampleMessageв шаблоне, вы можете видеть, как он работает, когда текст читается Hello Magento Stack Exchange.

введите описание изображения здесь

Если я удалю переопределение в файле шаблона exampleMessage, вернется к значению по умолчанию Hello?. Мне действительно нужно удалить var/view_preprocessedи pub/static/frontendпосле изменения этого, хотя. Я предполагаю, что Magento кешировал значение.

введите описание изображения здесь

Бен Крук
источник
Это будет работать в Magento2.1
Venkat
@Venkat - Вы имеете в виду, что теперь вы можете легко использовать Knockout без необходимости очищать узел? Или что мое исправление работает в 2.1?
Бен Крук
Ваше исправление будет работать в 2.1?
Венкат
Для меня привязки работают, но я получаю ошибку ссылки для первой привязки входных данных
Venkat
Я думаю, так как KnockoutJS, кажется, не сильно изменился с 2.0.X - я не пробовал это в 2.1, хотя, поэтому я не уверен на 100%. Также убедитесь, что вы проводите тщательное тестирование, так как я не уверен, что это лучший метод, хотя это единственный, который я могу найти.
Бен Крук