Étiquette : requireJS

[Magento 2] Les bases et patterns de l’utilisation de javascript (AMD, requireJS, widget jQuery, plugin, module, x-magento-init, etc…)

Intégrer et exploiter dans Magento 2 un plugin jQuery tiers dont le code source est structuré selon le pattern AMD

Pour illustrer cette partie, nous allons intégrer dans Magento 2 le plugin jQuery Slick Carousel, dont le code source est structuré selon le pattern AMD.

Remarque: l’intégration du plugin jQuery tiers va se faire dans un thème. Si vous comptez utiliser le plugin en question sur un projet multi-sites/multi-thèmes ou sur plusieurs projets, une meilleure stratégie consisterait à intégrer ce premier dans un nouveau module MyVendor_Slick qu’on placerait dans app/code/MyVendor/Slick.
Cette démarche n’est pas décrite ici, mais mériterait d’être envisagée en fonction de vos besoins.

Récupération du plugin sous Git et copie dans l’arborescence du thème

Récupérer la source minifiée du plugin Slick sous Git (tag v1.8.1 dans mon exemple, mais vous pouvez vérifier si il existe un tag plus récent) et copier-coller le fichier dans votre thème, au chemin suivant: app/design/frontend/MyVendor/mytheme/web/js/vendor/slick/slick.min.js.

Déclaration du plugin en tant que module via RequireJS

Dans le fichier app/design/frontend/MyVendor/mytheme/requirejs-config.js:

Création d’un fichier d’initialisation d’une instance du plugin

Dans votre thème, créer le fichier app/design/frontend/MyVendor/mytheme/web/js/init-slick.js qui va servir à appeller et à initialiser une instance du plugin javascript Slick et vous donnera accès à toutes les options de l’API originale:

Petite subtilité: ajouter une configuration spécifique par défaut à toutes les instances de votre plugin

Dans l’exemple ci-dessous, le fichier app/design/frontend/MyVendor/mytheme/web/js/init-slick.js est agrémenté d’un objet defaults qui surcharge (via les settings proposés de base par le plugin Slick Carousel prevArrow et nextArrow) l’aspect graphique des chevrons « slide précédent/slide suivant ».

L’intérêt d’ajouter une configuration spécifique par défaut à toutes les instances de votre plugin réside, pour l’exemple du Carousel, dans le fait qui si la charte graphique de votre projet prévoit les mêmes chevrons spés pour tous les sliders du site, vous allez pouvoir tous les initialiser sans avoir besoin de re-préciser à chaque fois que vous voulez surcharger les pictos fournis de base par ceux de votre charte.

Vous pouvez, bien entendu, les re-surcharger et utiliser d’autres options du plugin à la demande pour chaque nouveau Carousel que vous allez mettre en place.

Appeler et initialiser une instance du plugin sur un élément du DOM

Cette page de la documentation officielle vous expliquera comment appeler et initialiser une instance du plugin sur un élément du DOM dans Magento 2.

Elle aborde notamment la manière de procéder depuis un fichier template PHTML, depuis un fichier JS et comment exécuter data-amge-init et x-magento-init dans un cas où le DOM se met à jour dynamiquement.

Dans ce billet, je vais me contenter d’illustrer un exemple de notation déclarative avec x-magento-init depuis un fichier PHTML.

Exemple de notation déclarative avec x-magento-init depuis un fichier PHTML

Ici, j’initialise simplement mon module Slick sur l’élément du DOM qui porte l’ID home-slider. Le carousel va s’afficher soit avec les options de base fournies par le plugin Slick, soit avec les options de base fournies par le plugin Slick ET des pictos chevrons surchargés par ma configuration par défaut si j’en ai défini une.

Même exemple avec ajout d’options

Ici, je choisis de paramétrer mon carousel afin qu’il affiche 4 slides à la fois et qu’on scrolle d’une slide à chaque fois qu’on active les boutons prev/next ou tout autre mode de navigation fourni par le plugin. Les options slidesToShow et slidesToScroll sont fournies par le plugin Slick et exploitables immédiatement grâce à la magie d’AMD, de RequireJs et de Magento.

Ressources en ligne:

[Magento 2] Un module Javascript embarqué via RequireJS pour bloquer le scroll de la page à l’affichage d’un élément en surimpression

Testé fonctionnel Magento 2.4 CE. L’idée de ce billet est surtout de montrer comment Magento 2 permet l’accès à de petites fonctionnalités écrites en Javascript/jQuery de manière modulaire via RequireJS.

Le code source du plugin:

app/design/frontend///web/js/utils/page-scroll-state.js

Retenez le nom de la fonction handle() du plugin, car elle nous servira plus bas.

Déclarer le plugin via RequireJS:

app/design/frontend///requirejs-config.js

A noter: si l’on souhaite rendre notre plug-in disponible immédiatement dans toutes les pages du site, nous pouvons ajouter ceci au fichier requirejs-config.js (je ne sais pas si la présence d’une virgule en fin de ligne peut poser problème):

Exploiter le plugin dans un autre fichier JS, un PHTML, etc…:

  • ligne 3: on requiert notre plugin tel que déclaré dans la partie paths: du fichier app/design/frontend///requirejs-config.js
  • (ligne 2: on n’oublie pas la dépendance à jquery ou autre si besoin…)
  • ligne 4: on définit un alias pour notre plugin, à invoquer par la suite dans le code qui est encapsulé dans la fonction que nous ouvrons
  • lignes 10 et 18: on invoque la fonction .handle() de notre plugin, via l’alias pageScrollState (défini comme argument de la fonction en ligne 4)

Bonus: les styles!

[Magento 2] Un plugin jQuery custom pour rendre cliquable une zone complète plutôt que le seul lien qu’elle renferme avec x-magento-init et requireJS

Exemple d’utilisation: les cartes (items) catégories ou produits du listing catégories ou produits.

Rendre toute une zone cliquable, plutôt qu’uniquement le lien qu’elle contient. Exemple: une carte produit dans le listing produit. app/design/frontend/MyVendor/mytheme/Magento_Catalog/templates/product/list.phtml.

Utilisation dans un fichier PHTML:

Détail des fichiers à mettre en place:

app/web/app/design/frontend/MyVendor/mytheme/web/js/zone-click.js

Note: pour être sûr de ce que vous allez renseigner en paramètre exclude, vous pouvez ajouter dans le code ci-dessus un console.log($clickedElement.attr('class'));. Puis allez en front et cliquez sur les éléments à exclure. Les classes qui leur sont affectées apparaîtront en console (F12).

Déclaration via requireJS: app/design/frontend/MyVendor/mytheme/requirejs-config.js

app/design/frontend/MyVendor/mytheme/Magento_Catalog/templates/product/list.phtml

[Magento 2] Comment inclure et utiliser un fichier JS custom avec requireJS

Source: How to include and use custom js file with require js in magento2.

Source: Four Ways to Add JavaScript to Magento 2.

En-dehors d’un thème, en créant un module

Se référer à la documentation officielle de Magento 2 pour voir comment créer un nouveau module. Dans mon exemple, le vendor s’appelle Naked et le module s’appelle HelloWorld.

Dans app/code/Naked/HelloWorld/ créer une arborescence de dossiers et les trois fichiers suivants:

  • view/frontend/requirejs-config.js
  • view/frontend/web/js/hello-world.js
  • view/frontend/web/js/methods.js

view/frontend/requirejs-config.js

Les scripts déclarés sous la variable deps vont fonctionner immédiatement et sur toutes les pages du site. Inutile d’initialiser quoi que ce soit dans un fichier *.phtml.

view/frontend/web/js/hello-world.js

view/frontend/web/js/methods.js

[Magento 2] Ajouter le framework Foundation 6 for sites dans un thème et rendre ses widgets javascript disponibles avec RequireJS

Pour ce tuto, nous allons intégrer la version 6.5.3 de Foundation for sites. Le pre-processing des assets se fait avec Webpack et Gulp (contre Grunt dans Magento 2) et les fichiers source sont fournis au format SASS pour les styles et ES5, jQuery (version 3+) pour les javascripts, ce qui les rend inexploitables de base avec Magento 2 (LESS pour les styles et version de jQuery 1.12 pour les javascripts). Nous utiliserons donc les versions déjà compilées (CSS et javascript « vanilla ») qui se trouvent dans le dossier dist du repository Git.

Les styles

Créer un dossier app/design/<Vendor_name>/<theme_name>/web/css/vendor/foundation/ dans lequel nous allons copier/coller l’intégralité des fichiers se trouvant dans le chemin dist/css du Git.

Dans app/design/<Vendor_name>/<theme_name>/web/css/source/_theme.less:

A noter: le code CSS final du framework n’étant pas éclaté en différents fichiers, il faudra probablement que vous ré-éditiez ce fichier pour retirer les styles qui entrent en conflit avec ceux de votre thème. Vous pouvez importer foundation.min.css si vous souhaitez conserver le fichier en l’état.

Les scripts

Joindre l’intégralité des widgets

Solution la plus simple: la version minifiée dans l’intégralité du code JS du framework tient en 171ko.

Créer un dossier app/design/<Vendor_name>/<theme_name>/web/js/vendor/foundation/ dans lequel nous allons copier/coller l’intégralité des fichiers se trouvant dans le chemin dist/js du Git.

Dans app/design/<Vendor_name>/<theme_name>/requirejs-config.js:

Dans un fichier *.phtml (ou *.js pour la partie entre <script />):

Problématiques:

Modifier un paramètre du composant et inclure une chaîne de traduction i18n

Avant d’instancier un nouvel objet, on modifier un ou plusieurs paramètres par défaut. La traduction se fait par la méthode PHP de Magento 2 (et pas par la méthode Javascript).

[RequireJS] Astuces, bonnes pratiques

Apprendre les bases

Doc officielle

La documentation officielle de RequireJS avec un chapitre sur les erreurs les plus communes.

Tutoriels

RequireJS et le code JavaScript modulaire (tutoriel en français).

Tuto très clair de codedamn sur YouTube et code source. Permet d’aborder les bases avec:

  • Le fichier de config
  • La fonction require
  • La fonction define
  • AMD et les modules

Troubleshooting

La console me remonte cette erreur: require.js:166 Uncaught Error: Mismatched anonymous define(): dans le code source, remplacer define par require!

[Magento 2] Importer les points de rupture CSS LESS pour une exploitation avec javascript jQuery

Source: Importing CSS Breakpoints Into JavaScript (adapté pour Magento 2).

!important: ce tuto demande à créer/modifier un certain nombre de fichiers. Le respect de l’organisation de ces derniers dans l’arborescence de votre thème est capital pour le bon fonctionnement de la fonctionnalité présentée. Vous pourrez bien-sûr modifier les emplacements des fichiers, mais pensez dans ce cas à mettre à jour tous les chemins en conséquence.

L’arborescence utilisée dans le code source fourni est mise à plat plus bas.

Intérêt pour le développeur Magento 2 d’importer les breakpoints LESS pour une exploitation avec javascript?

Magento 2 permet, par le biais de l’outil matchMedia.js, de déclencher du code javascript/jQuery en fonction d’un point de rupture. Extrait de la documentation officielle à ce sujet:

On remarque que la valeur du point de rupture est renseignée de manière statique (768px): media: '(min-width: 768px)'. Ce procédé a ses limites. Le jour où la valeur de ce breakpoint est modifiée dans la feuille de style, il faudra mettre à jour à la main toutes les déclarations faites avec matchMedia.

Le but est donc de rendre dynamique la valeur de media:.

Partie LESS:

Si il n’existe pas encore dans votre thème, créer un dossier app/design/frontend/<Vendor_name>/<theme_name>/web/css/vendor/less-curious/ et y placer, à la racine, l’intégralité des fichiers disponibles sur ce repository Git.

A ce stade, il est surtout important que les fichiers for.less et for-each.less soient accessibles respectivement depuis les chemins suivants:

  • app/design/frontend/<Vendor_name>/<theme_name>/web/css/vendor/less-curious/src/for.less
  • app/design/frontend/<Vendor_name>/<theme_name>/web/css/vendor/less-curious/src/for-each.less
  • Note: le repo Git pouvant disparaître du jour au lendemain, un backup au format ZIP est disponible ici 😉.
  • Note #2: si vous regardez le code source du projet less-curious, vous remarquerez peut-être que ce dernier est déprécié et que l’auteur fournit un lien vers une version plus actuelle, full JS à intégrer avec un pré-processeur comme Grunt. Problème: j’ai eu beau suivre les bonnes pratiques d’intégration d’un plug-in pour grunt-contrib-less dans le système mis en place dans Magento 2, je n’ai rien réussi à compiler… UPDATE (pas testé) 05/11/2019: la solution se situe peut-être dans la mise en place d’une configuration custom de Grunt.

Si il n’existe pas encore dans votre thème, créer un dossier app/design/frontend/<Vendor_name>/<theme_name>/web/css/source/devtools/.

Créer le fichier app/design/frontend/<Vendor_name>/<theme_name>/web/css/source/devtools/_js-mediaqueries.less et placez-y le code ci-dessous:

Dans le fichier app/design/frontend/<Vendor_name>/<theme_name>/web/css/source/_theme.less, ajouter la ligne suivante JUSTE EN-DESSOUS des potentielles surcharges que vous avez faites des variables pré-déclarées dans le(s) thème(s) parent(s) dont votre thème va hériter:

Partie JS:

Créer le fichier app/design/frontend/<Vendor_name>/<theme_name>/web/js/js-mediaqueries.js:

Créer ou adapter le fichier app/design/frontend/<Vendor_name>/<theme_name>/requirejs-config.js:

Partie XML:

Créer le fichier app/design/frontend/<Vendor_name>/<theme_name>/Magento_Theme/layout/default_head_blocks.xml:

Note: si ce fichier existe déjà dans votre thème, ajouter simplement la ligne <script src="js/js-mediaqueries.js" /> entre les balises <head>.

Commandes à exécuter en bash:

  • Un cache:clean
  • $ rm -rf pub/static/*; rm -rf var/view_preprocessed/*

Un process Grunt doit tourner ou une compilation des assets front-end via une commande Magento doit être faite.

Arborescence complète, selon les fichiers de ce tuto:

  • app/design/frontend/[Vendor_name]/[theme_name]/
    • Magento_Theme/
      • layout/
        • default_head_blocks.xml
    • web/
      • css/
        • source/
          • devtools/
            • _js-mediaqueries.less
          • vendor/
            • less-curious/
              • src/
                • for.less
                • for-each.less
          • _theme.less
      • js/
        • js-mediaqueries.js
    • requirejs-config.js

[Magento 2] Exploiter matchMedia pour passer des paramètres différents à un widget javascript en fonction du breakpoint

Ressources en ligne:

Attention: en work-in-progress… Au chargement de la page, selon qu’on soit en vue Mobile ou Desktop, les paramètres correspondants sont bien pris en compte mais il reste encore pas mal de soucis.

Doc officielle de Magento 2 relative à l’utilisation de matchMedia: JavaScript in Magento responsive design.

Fichier du core de Magento 2 à étudier: lib/web/mage/menu.js qui exploite matchMedia via l’alias mediaCheck et la brique $.ui.menu de jQueryUI.

Ressources externes, documentation…

TODOs

  • lorsqu’on change de breakpoint sans recharger la page, les paramètres du widget ne sont pas mis à jour pour la nouvelle vue correspondante (voir pour une méthode destroy ou (mieux) refresh (edit: refresh semble ne pas vouloir fonctionner avec Magento 2).
  • la fonction bindAccordion ne permet pas l’ajout de méthodes ou d’événements (edit: j’ai dégagé la fonction)
  • cherry on the cake: le breakpoint courant devrait être détecté automatiquement (changement après chargement de page compris)

Mise en place:

\app\design\frontend\<Vendor_name>\<theme_name>\web\js\accordion-config.js

\app\design\frontend\<Vendor_name>\<theme_name>\requirejs-config.js

\app\design\frontend\<Vendor_name>\<theme_name>\Magento_LayeredNavigation\templates\layer\view.phtml


HTML

JS

[Magento 2] Utiliser des media queries, points de rupture, en LESS (CSS) et en javascript avec matchMedia

Exploiter les media queries dans les fichiers styles LESS (CSS)

Variables de breakpoints

Les surcharges de valeurs de breakpoints existants (déjà déclarés dans un thème parent) se font dans le fichier _theme.less de votre thème enfant.

Utilisation des mixins .media-width fournis par Magento 2 et code compilé

Les fichiers CSS styles-m.less et styles-l.less sont auto-générés par Magento 2 lors de la compilation des fichiers *.less. Il ne faut PAS les recréer à la main dans vos thèmes enfants. Si on respecte les bonnes pratiques de déclaration des media-queries dictées par la doc officielle, on peut déclarer autant de fois qu’on veut un même breakpoint dans nos sources *.less et toutes nos déclarations seront regroupées sous une seule dans les fichiers *.css finaux. Exemple:

Avoir recours aux media queries dans les fichiers JS avec matchMedia

Doc officielle – JavaScript in Magento responsive design.

Utiliser matchMedia directement dans une vue *.phtml

Le code ci-dessous affiche une bordure rose sur les écrans de Mobile (dont la résolution est inférieure à 768px) et bleue sur les écrans dont la résolution est plus large.

Utiliser matchMedia depuis un fichier *.js

Attention: de part la nature « protectrice » de requireJS, le code encapsulé dans votre fichier Js ne sera pris en compte qu’après avoir été déclaré dans une vue *.phtml ou (mauvaise pratique) dans un JS inclus à l’arrache (dans app\design\frontend\<Vendor_name>\<theme_name>\Magento_Theme\layout\default_head_blocks.xml par exemple) et non-contrôlé par requireJS.

Dans mon exemple, je crée un fichier app\design\frontend\<Vendor_name>\<theme_name>\web\js\mediaqueries.js:

Attention, à retenir: il faut bien encapsuler le code qui doit être exécuté au chargement de la page dans une fonction anonyme $(function(){ // mon code ici... });. Sinon, pas d’exécution. C’est du jQuery 😉

Dans le fichier app\design\frontend\<Vendor_name>\<theme_name>\requirejs-config.js, je déclare mon nouveau fichier mediaqueries.js et je le rends disponible pour requireJS:

Dans un fichier *.phtml, je fais exécuter le code se trouvant dans le fichier mediaqueries.js:

[Magento 2] Exploiter le widget javascript jQuery natif Accordion dans le fichier PHTML des filtres produit (page category)

Note: ce widget Accordion fait partie d’une collection étendue de widgets fournis de base par Magento 2 (à ne pas confondre avec la bibliothèque UI Magento 2, fournie de base également, et qu’il convient de connaître aussi.

Pour ce tuto, nous allons afficher les différentes familles de tri produits (catégorie, taille, activity, color, erin recommends, genre, material, nouveau, prix, etc…) sous forme d’accordéons. De base, ils s’affichent tous ouverts, y-compris en vue Mobile ce qui force l’utilisateur à scroller bas dans la page avant de voir apparaître le premier produit. Le fonctionnalités voulues pour ce accordéon:

  • tous les accordéons (différentes familles de tri) sont ouverts au chargement de la page
  • plusieurs accordéons peuvent rester ouverts simultanément

(Note pour plus tard: il peut être intéressant, dans le cadre de la découverte des media-queries côté JS, d’initialiser ce widget avec des options différentes en fonction de l’affichage mobile/desktop).

A noter: il existe plusieurs méthodes pour initialiser du code javascript dans une vue dans Magento 2. Nous allons nous concentrer sur deux d’entre-elles qui ne sont ni meilleures, ni moins bonnes que les autres.

Imperative notation

En initialisant notre widget directement dans un fichier *.phtml, entre balises <script type="text/javascript">.

Commencer par éditer le fichier *.phtml app\design\frontend\<Vendor_name>\<theme_name>\Magento_LayeredNavigation\templates\layer\view.phtml qui constitue la base de la vue des différents filtres. Notez bien l’ajout, par rapport à la vue que nous surchargeons, des attributs data-role="collapsible", data-role="trigger" et data-role="content" respectivement placés sur les éléments dt, a et dd, ainsi que de la classe filter-options-collapsible-trigger placée sur le nouvellement ajouté élément a.

Le widget s’initialise ensuite comme suit avec requireJS (code placé dans le même fichier view.phtml, immédiatement après la ligne <?php if ($block->canShowBlock()): ?>):

Declarative notation

Le widget va s’initialiser sans avoir recours à un require déclaré dans une balise <script />, par de biais d’un attribut data-mage-init placé directement sur l’élément à partir duquel nous venons précédemment d’initialiser notre widget à la manière de jQuery.

Dans les deux cas on n’oublie pas de lancer un cache:clean en console!