Cómo usar Knockout JS en Magento 2

12

Mi problema:

Estoy intentando escribir una pequeña aplicación Knockout JS dentro de Magento 2, estoy luchando por inicializar la aplicación, ya que cuando la uso ko.applyBindings(AppViewModel, document.getElementById("koTest"));rompe la Knockout utilizada por Magento y arroja este error:

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

Sospecho que es por:

Sospecho que esto se debe a que Magento 2 ya usa ko.applyBindings()dentro app/code/Magento/Ui/view/base/web/js/lib/knockout/bootstrap.js. Y como eso no especifica un nodo que no puedo usar de ko.applyBindingsnuevo.

Si no uso ko.applyBindings(AppViewModel, document.getElementById("koTest"))mi código, entonces mi aplicación no se inicializa.

Esto me hace pensar que necesito usar de alguna manera el ko.applyBindings()archivo knockout / bootstrap.js pero no tengo idea de cómo, ¿alguien puede ayudarme? Tengo poca experiencia con Knockout.

Mi código

<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
fuente
1
Aquí hay un tutorial: inchoo.net/magento-2/knockout-js-in-magento-2
Aaron Allen

Respuestas:

23

Método simple donde NO necesita usar plantillas html

Gracias a Vinai Kopp, finalmente obtuve una respuesta a esto, es mucho más simple que mi solución alternativa anterior (estaba limpiando nodos). Todo lo que necesita hacer es definir 'ko'como una dependencia y agregar su código dentro de una función de retorno.

A continuación se muestra un ejemplo simple que muestra parte del texto pasado a través de JSON.

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

Aquí le decimos a Magento el alcance de nuestros componentes (esto debe coincidir data-bind: "scope: 'example-scope'"y pasar cualquier información adicional. Esta podría ser la URL base, un mensaje simple, casi cualquier cosa que desee. He pasado una cadena (PHP echo) como ejemplo

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

Y aquí escribimos nuestro 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

ingrese la descripción de la imagen aquí

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

Método donde necesita usar plantillas HTML

Si desea utilizar el sistema de plantillas HTML dentro de Magento2 / Knockout (que supongo que necesitará para cualquier trabajo significativo), debe realizar algunos cambios en comparación con mi respuesta simplificada (a continuación).

Si no necesita la funcionalidad de la plantilla, desplácese hacia abajo hasta mi antigua respuesta simplificada.

Los archivos que estoy usando para este ejemplo son:

  • 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

El archivo de plantilla PHTML

El único cambio en nuestra plantilla PHTML es la llamada a la getTemplate()función:

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

El archivo JS (componente)

Hay algunos cambios que deberá realizar en el archivo JS, los detallaré a continuación.

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 - Su función de retorno ahora necesita extender el módulo uiComponent:

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

2 - Necesita agregar una initializefunción y llamar this._super(). this._super()llamará a la función del componente principal con el mismo nombre. Así que en este caso lo que se llamará initializede uiComponent.

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

3 - Opcional : también puede establecer algunos valores predeterminados para su componente aquí, creo que es una buena práctica a seguir, ya que hace que su componente sea fácil de trabajar. Cuando lo reutiliza, puede mantener los valores predeterminados o, si desea personalizarlo, puede llamarlo con nuevos argumentos sin alterar el componente.

Por ejemplo, si nos fijamos en los valores predeterminados de la JS establece exampleMessageque 'Hello?'sin embargo, la página se visualice el texto Hello Magento Stack Exchange!. Esto se debe a que he sobrescrito exampleMessageen el archivo PHTML cuando llamé al componente.

La plantilla HTML

Todavía tengo que investigar y ver de qué son capaces las plantillas HTML, supongo que las características mencionadas en la documentación de Knockout JS se pueden usar aquí, lo que las hace bastante flexibles.

Acabo de agregar un poco de texto lorem ipsum por ahora, probablemente proporcionaré otra pregunta / respuesta una vez que haya descubierto lo que pueden hacer las plantillas 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!

El resultado y sobrescribir los valores predeterminados

Como se mencionó anteriormente, puede ver que he sobrescrito exampleMessagedentro de la plantilla, puede ver que funciona mientras se lee el texto Hello Magento Stack Exchange.

ingrese la descripción de la imagen aquí

Si elimino la anulación en el archivo de plantilla exampleMessage, volverá a su valor predeterminado Hello?. Necesité eliminar var/view_preprocessedy pub/static/frontenddespués de cambiar esto sin embargo. Supongo que Magento había almacenado en caché el valor.

ingrese la descripción de la imagen aquí

Ben Crook
fuente
Esto funcionará en Magento2.1
Venkat
@Venkat: ¿Quiere decir que ahora puede usar Knockout fácilmente sin tener que limpiar el nodo? ¿O que mi solución funciona en 2.1?
Ben Crook
Su solución funcionará en 2.1?
Venkat
Para mí, los enlaces funcionan pero se obtiene un error de referencia para el primer enlace de datos de entrada
Venkat
Creo que, dado que KnockoutJS no parece haber cambiado mucho desde 2.0.X, no lo he probado en 2.1, así que no estoy 100% seguro. También asegúrese de hacer algunas pruebas exhaustivas ya que no estoy seguro de si este es el mejor método, aunque es el único que puedo encontrar.
Ben Crook