Como usar o Knockout JS no Magento 2

12

Meu problema:

Estou tentando escrever um pequeno aplicativo Knockout JS no Magento 2, estou tendo dificuldades para inicializar o aplicativo, pois quando o uso ko.applyBindings(AppViewModel, document.getElementById("koTest"));quebra o Knockout usado pelo Magento e gera este erro:

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

Eu suspeito que é por causa de:

Eu suspeito que isso ocorre porque o Magento 2 já usa ko.applyBindings()dentro app/code/Magento/Ui/view/base/web/js/lib/knockout/bootstrap.js. E como isso não especifica um nó, não posso usar ko.applyBindingsnovamente.

Se eu não usar ko.applyBindings(AppViewModel, document.getElementById("koTest"))no meu código, meu aplicativo não será inicializado.

Isso me faz pensar que, de alguma forma, preciso usar o ko.applyBindings()arquivo in knockout / bootstrap.js, mas não sei como, alguém pode ajudar? Tenho pouca experiência com o Knockout.

My Code

<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>
Ben Crook
fonte
1
Há um tutorial aqui: inchoo.net/magento-2/knockout-js-in-magento-2
Aaron Allen

Respostas:

23

Método simples em que você NÃO precisa usar modelos html

Graças a Vinai Kopp, finalmente consegui uma resposta para isso, é muito mais simples do que minha solução hacky anterior (eu estava limpando nós). Tudo que você precisa fazer é definir 'ko'como uma dependência e adicionar seu código dentro de uma função de retorno.

Abaixo está um exemplo simples que renderiza algum texto transmitido via JSON.

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

Aqui, informamos ao Magento o escopo de nossos componentes (isso deve corresponder data-bind: "scope: 'example-scope'"e passar dados adicionais. Esse pode ser o URL base, uma mensagem simples, praticamente o que você desejar. Passei uma string (eco do PHP) como exemplo

<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>

E aqui escrevemos nosso 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);
    }
});

 Resultado

insira a descrição da imagem aqui

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

Método em que você precisa usar modelos HTML

Se você deseja usar o sistema de modelos HTML no Magento2 / Knockout (que eu presumo que você precisará para trabalhos significativos), existem algumas mudanças que você precisará fazer em comparação com a minha resposta simplificada (abaixo).

Se você não precisar da funcionalidade do modelo, role para baixo até a minha antiga resposta simplificada.

Os arquivos que estou usando para este exemplo são:

  • 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

O arquivo de modelo PHTML

A única alteração no nosso modelo PHTML é a chamada para a getTemplate()função:

<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>

O arquivo JS (componente)

Existem algumas alterações que você precisará fazer no arquivo JS, que serão detalhadas abaixo.

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 - Sua função de retorno agora precisa estender o módulo uiComponent:

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

2 - Você precisa adicionar uma initializefunção e chamada this._super(). this._super()chamará a função do componente pai com o mesmo nome. Então, neste caso eu acho que ele irá chamar initializede uiComponent.

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

3 - Opcional - Você também pode definir alguns padrões para o seu componente aqui, acho que é uma boa prática a seguir, pois facilita o trabalho com o seu componente. Ao reutilizá-lo, você pode manter os padrões ou, se desejar personalizá-lo, pode chamá-lo com novos argumentos sem alterar o componente.

Por exemplo, se você observar os padrões no JS, ele define exampleMessagecomo 'Hello?'ainda a página está renderizando o texto como Hello Magento Stack Exchange!. Isso ocorre porque eu substituí exampleMessageo arquivo PHTML quando chamei o componente.

O modelo HTML

Ainda estou procurando e vendo do que os modelos HTML são capazes, presumo que os recursos mencionados na documentação do Knockout JS possam ser usados ​​aqui, tornando-os bastante flexíveis.

Acabei de adicionar um texto de lorem ipsum por enquanto, provavelmente fornecerei outra pergunta / resposta depois de descobrir o que os modelos HTML podem fazer.

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!

O resultado e substituindo os padrões

Como mencionado anteriormente, você pode ver que eu sobrescrevi exampleMessageno modelo, você pode vê-lo funcionando enquanto o texto é lido Hello Magento Stack Exchange.

insira a descrição da imagem aqui

Se eu remover a substituição no arquivo de modelo, exampleMessageela retornará ao padrão Hello?. Eu precisava excluir var/view_preprocessede pub/static/frontenddepois de alterar isso. Presumo que Magento tenha armazenado em cache o valor.

insira a descrição da imagem aqui

Ben Crook
fonte
Isso funcionará no Magento2.1
Venkat
@Venkat - Você quer dizer que agora você pode usar facilmente o Knockout sem precisar limpar o nó? Ou que minha correção funciona no 2.1?
Ben Crook
Sua correção funcionará no 2.1?
Venkat
Para mim ligações é trabalhado mas recebendo erro de referência para os dados de primeira entrada de ligação
Venkat
Eu acho que o KnockoutJS parece não ter mudado muito desde o 2.0.X - eu ainda não tentei o 2.1, então não tenho 100% de certeza. Além disso, certifique-se de fazer alguns testes completos, pois não tenho certeza se esse é o melhor método, mas é o único que posso encontrar.
Ben Crook