Comment utiliser Knockout JS dans Magento 2

12

Mon problème:

J'essaie d'écrire une petite application Knockout JS dans Magento 2, j'ai du mal à initialiser l'application car lorsque je l'utilise, ko.applyBindings(AppViewModel, document.getElementById("koTest"));cela brise le Knockout utilisé par Magento et génère cette erreur:

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

Je soupçonne que c'est à cause de:

Je soupçonne que c'est parce que Magento 2 utilise déjà à l' ko.applyBindings()intérieur app/code/Magento/Ui/view/base/web/js/lib/knockout/bootstrap.js. Et comme cela ne spécifie pas un nœud que je ne peux plus utiliser ko.applyBindings.

Si je n'utilise pas ko.applyBindings(AppViewModel, document.getElementById("koTest"))dans mon code, mon application ne s'initialise pas.

Cela me fait penser que je dois en quelque sorte utiliser le ko.applyBindings()fichier in knockout / bootstrap.js mais je ne sais pas comment, quelqu'un peut-il aider? J'ai peu d'expérience avec Knockout.

Mon 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
la source
1
Il y a un tutoriel ici: inchoo.net/magento-2/knockout-js-in-magento-2
Aaron Allen

Réponses:

23

Méthode simple où vous n'avez PAS besoin d'utiliser des modèles html

Grâce à Vinai Kopp, j'ai enfin une réponse à cela, c'est beaucoup plus simple que ma solution de contournement précédente (je nettoyais les nœuds). Il vous suffit de définir 'ko'comme une dépendance et d'ajouter votre code dans une fonction de retour.

Vous trouverez ci-dessous un exemple simple qui restitue du texte transmis via JSON.

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

Ici, nous indiquons à Magento la portée de nos composants (cela doit correspondre data-bind: "scope: 'example-scope'"et transmettre toutes les données supplémentaires. Cela pourrait être l'URL de base, un message simple, à peu près tout ce que vous voulez. J'ai passé une chaîne (écho PHP) comme exemple

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

Et ici, nous écrivons notre 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);
    }
});

 Résultat

entrez la description de l'image ici

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

Méthode où vous avez besoin d'utiliser des modèles HTML

Si vous souhaitez utiliser le système de création de modèles HTML dans Magento2 / Knockout (dont je suppose que vous aurez besoin pour tout travail important), vous devrez apporter quelques modifications par rapport à ma réponse simplifiée (ci-dessous).

Si vous n'avez pas besoin de la fonctionnalité de modèle, faites défiler vers le bas jusqu'à mon ancienne réponse simplifiée.

Les fichiers que j'utilise pour cet exemple sont:

  • 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

Le fichier modèle PHTML

Le seul changement à notre modèle PHTML est l'appel à la getTemplate()fonction:

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

Le fichier JS (composant)

Il y a quelques changements que vous devrez apporter au fichier JS, je vais les détailler ci-dessous.

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 - Votre fonction de retour doit maintenant étendre le module uiComponent:

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

2 - Vous devez ajouter une initializefonction et appeler this._super(). this._super()appellera la fonction du composant parent avec le même nom. Donc , dans ce cas , je pense qu'il appellera initializede uiComponent.

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

3 - Facultatif - Vous pouvez également définir des valeurs par défaut pour votre composant ici, je pense que c'est une bonne pratique à suivre car cela rend votre composant facile à utiliser. Lorsque vous le réutilisez, vous pouvez conserver les valeurs par défaut ou si vous souhaitez le personnaliser, vous pouvez l'appeler avec de nouveaux arguments sans modifier le composant.

Par exemple, si vous regardez les valeurs par défaut dans la JS il met exampleMessageà 'Hello?'encore la page est rendu le texte Hello Magento Stack Exchange!. En effet, j'ai remplacé exampleMessagele fichier PHTML lorsque j'ai appelé le composant.

Le modèle HTML

Je dois encore creuser et voir de quoi les modèles HTML sont capables, je suppose que les fonctionnalités mentionnées dans la documentation Knockout JS peuvent être utilisées ici pour les rendre assez flexibles.

Je viens d'ajouter du texte lorem ipsum pour le moment, je fournirai probablement une autre question / réponse une fois que j'aurai compris ce que les modèles HTML peuvent faire.

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!

Le résultat et le remplacement des valeurs par défaut

Comme mentionné précédemment, vous pouvez voir que j'ai remplacé exampleMessagele modèle, vous pouvez le voir fonctionner pendant la lecture du texte Hello Magento Stack Exchange.

entrez la description de l'image ici

Si je supprime le remplacement dans le fichier modèle, exampleMessageil reviendra à sa valeur par défaut Hello?. J'ai dû supprimer var/view_preprocessedet pub/static/frontendaprès avoir changé cela cependant. Je suppose que Magento a mis en cache la valeur.

entrez la description de l'image ici

Ben Crook
la source
Cela fonctionnera dans Magento2.1
Venkat
@Venkat - Voulez-vous dire que vous pouvez désormais utiliser facilement Knockout sans avoir à nettoyer le nœud? Ou que mon correctif fonctionne en 2.1?
Ben Crook
Votre correctif fonctionnera en 2.1?
Venkat
Pour moi, les liaisons fonctionnent mais obtiennent une erreur de référence pour la première liaison de données d'entrée
Venkat
Je pense que KnockoutJS ne semble pas avoir beaucoup changé depuis 2.0.X - Je ne l'ai pas essayé en 2.1, donc je ne suis pas sûr à 100%. Assurez-vous également de faire des tests approfondis car je ne suis pas sûr que ce soit la meilleure méthode, c'est la seule que je puisse trouver.
Ben Crook