Attention: pas testé (pas eu besoin au final…), mais la méthode me parait propre en regard des bonnes pratiques de coding de Magento 2.
Source: How to access custom attributes from the current product in JavaScript?.
Attention: pas testé (pas eu besoin au final…), mais la méthode me parait propre en regard des bonnes pratiques de coding de Magento 2.
Source: How to access custom attributes from the current product in JavaScript?.
Objectif: créer un widget Toggle flexible dont le rôle sera uniquement d’affiche/masquer des éléments du DOM lorsque les utilisateurs interagissent avec un autre élément d’interface (bouton, checkbox, autre…).
Source: Marking elements expandable using aria-expanded. Exemple d’utilisation sur l’élément HTML Button. Exemple d’utilisation sur l’élément HTML Checkbox.
Source: https://www.accessibility-developer-guide.com/.
1 2 3 4 5 6 7 |
<p> Please click the following checkbox. </p> <input id="checkbox" type="checkbox" /><label for="checkbox">Show tooltip</label> <div hidden="" id="tooltip"> Thanks for toggling! Please click again to hide me. </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
;(function () { $(document).ready(function () { var $checkbox, $tooltip $checkbox = $('input') $tooltip = $('#tooltip') return $checkbox.change(() => { if ($tooltip.attr('hidden') === 'hidden') { return $tooltip.removeAttr('hidden') } else { return $tooltip.attr('hidden', true) } }) }) }.call(this)) |
wcag-toggle.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// Source: https://www.accessibility-developer-guide.com/examples/sensible-aria-usage/expanded/ define([ 'jquery', 'jquery-ui-modules/widget' ], function($) { 'use strict'; $.widget('mage.wcagToggle', { options: { toggleableElement: '' }, /** * Create widget * @private */ _create: function() { // console.log("%c wcagToggle", "background: yellow"); this.element.on('click', $.proxy(this._toggleOnOff, this)); // ...is similar to: // this.element.on('click', this._toggleOnOff.bind(this)); }, /** * Toggle On/Off * @private */ _toggleOnOff: function() { if ($(this.options.toggleableElement).attr('hidden') === 'hidden') { $(this.options.toggleableElement).removeAttr('hidden'); this.element.attr('aria-expanded', true); } else { $(this.options.toggleableElement).attr('hidden', true); this.element.attr('aria-expanded', false); } } }); return $.mage.wcagToggle; }); |
PHTML
:Partie DOM (attention, je mets juste les grandes lignes). Se référer à l’example de code proposé par le site source.
1 2 3 4 5 6 7 |
<div class="field choice newsletter"> <input type="checkbox" id="is_subscribed" /> <label for="is_subscribed" class="label"></label> </div> <div id="is_subscribed_is_checked" hidden=""> </div> |
1 2 3 4 5 6 7 8 9 |
<script type="text/x-magento-init"> { "#is_subscribed": { "wcagToggle": { "toggleableElement": "#is_subscribed_is_checked" } } } </script> |
requirejs-config.js
:
1 2 3 4 5 6 7 |
var config = { map: { '*': { wcagToggle: 'Magento_Theme/js/widgets/wcag-toggle' } } }; |
Le markup HTML par défaut pour l’affichage du prix (dans le fiche produit par exemple) est régi par un widget mage.priceBox qu’on peut retrouver ici: view/base/web/js/price-box.js
. C’est l’option priceTemplate
qui définit le markup par défaut <span class="price"><%- data.formatted %></span>
.
Pour le surcharger, dans app/design/frontend/<Vendor>/<theme>/Magento_Catalog/templates/product/price/amount/default.phtml
, rajouter à la fin:
1 2 3 4 5 6 7 8 9 |
<script type="text/x-magento-init"> { "[data-role=priceBox]": { "Magento_Catalog/js/price-box": { "priceTemplate": "<strong class=\"price\"><%- data.formatted %></strong>" } } } </script> |
Testé fonctionnel Magento 2.4
Sources: Best way to extend module less found in web/css/source/module, like _minicart.less? et Extend or override parent, components or modules styles in Magento 2 et version PDF pour la postérité.
Fichier parent: app/design/frontend/Vendor/default/Magento_Catalog/web/css/source/module/listings/_prod-small.less
.
Son extension: app/design/frontend/Vendor/germany/Magento_Catalog/web/css/source/module/listings/_prod-small_extend.less
L’import de l’extension: app/design/frontend/Vendor/germany/Magento_Catalog/web/css/source/_extend.less
1 |
@import 'module/listings/_prod-small_extend.less'; |
Source: How to get Customer Address in formated way and print it – Magento2.3 et version PDF pour la postérité.
Pour les lignes d’adresse manquantes, il faut aller en BO, choisir le scope et effectuer la manip’ décrite dans la méthode 2 du tuto.
J’ai pour ma part modifié les Address templates Text et HTML. Il faut commencer par décocher les cases « Use Default » pour les champs que tu veux modifier, puis:
Pour Text, remplacer:
1 |
{{if street1}}{{var street1}}{{/if}} |
…par:
1 |
{{if street1}}{{var street1}}{{/if}}{{if street2}}{{var street2}}{{/if}}{{if street3}}{{var street3}}{{/if}} |
Pour HTML, remplacer:
1 |
{{if street1}}{{var street1}}<br/>{{/if}} |
…par:
1 |
{{if street1}}{{var street1}}<br/>{{/if}}{{if street2}}{{var street2}}<br/>{{/if}}{{if street3}}{{var street3}}<br/>{{/if}} |
Avec ça:
Sources:
foreach
Extrait du fichier: app/design/frontend/Vendor/theme/Magento_Checkout/web/js/view/address-renderer-mixin.js
1 2 3 4 5 6 |
/** * @return {string} */ formatStreet: function (str) { return String(str).replace(/,(?=[^\s])/g, ", "); }, |
Extrait du fichier: app/design/frontend/Vendor/theme/Magento_Checkout/web/template/shipping-information/address-renderer/default.html
1 |
<text args="formatStreet(address().street)"></text> |
Résultat en front:
Street 1,Street 2,Street 3
1 2 3 4 |
<each args="data: address().street, as: 'streetLine'"> <text args="streetLine"></text> <br> </each> |
Résultat en front:
Street 1
Street 2
Street 3
L’intérêt de ce billet est de donner quelques astuces pour adapter rapidement du code javascript aux standards jQuery UI Widget pour une meilleure exploitation dans Magento 2.
Télécharger les sources: Magento2-jQuery-widget-example.
Dans cette archive au format ZIP, deux fichiers:
Note: très bonne ressource, en marge de la doc officielle, pour créer un widget jQuery UI pour Magento 2.
Note: notre fichier d’exemple est placé dans un thème Magento 2 suivant le chemin: app/design/frontend/MyVendor/mytheme/web/js/attribute-carousel.js
On commence par un bon vieux define
qui nous servira à définir notre widget en tant que de module via RequireJS. Il pourra ensuite s’articuler avec d’autres modules définis en tant que tels, toujours via RequireJS:
1 2 3 4 5 6 7 8 |
define([ 'jquery', // obligatoire 'jquery-ui-modules/widget', // obligatoire 'autre module...', // facultatif 'autre module...', // facultatif 'slick' // le plugin tiers jQuery AMD Slick Carousel ], function($) { 'use strict'; |
Déclaration ici des variables globales pour ce widget (facultatif, votre widget n’en a peut-être pas besoin):
1 2 |
let ATTRIBUTE_SELECTOR_ELEMENT_ID, SELECTED_OPTION_INDEX; |
Déclaration du widget sous la forme $.widget('mage.<nomDuWidget>', {:
1 |
$.widget('mage.nomDuWidget', { |
Déclaration des options du widget sous forme d’objet options
, si il exploite des arguments (facultatif).
Le libellé options
ne doit pas être modifié!
(voir aussi les commentaires directement dans le bout de code ;))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
options: { // Option "simple": attributeSelectorId: '[id^="attribute"]', // Passage de plusieurs paramètres sous forme de sous-objet "slick" (par exemple, si on souhaite exploiter un plugin jQuery tiers qui propose des paramètres): slick: { infinite: false, slidesToShow: 3, slidesToScroll: 3, prevArrow: '<button type="button" class="clear slick-prev"><svg viewBox="0 0 100 100" class="icon icon-arrow-back-chevron"><use xlink:href="#icon-arrow-back-chevron"></use></svg></button>', nextArrow: '<button type="button" class="clear slick-next"><svg viewBox="0 0 100 100" class="icon icon-arrow-forward-chevron"><use xlink:href="#icon-arrow-forward-chevron"></use></svg></button>' } }, // <= ATTENTION A NE PAS OUBLIER LA VIRGULE JUSTE LA! |
La fonction privée _create"
(présence obligatoire), qui sera automatiquement exécutée à chaque initialisation de ce widget depuis un PHTML, un fichier JS, …
Le libellé _create
ne doit pas être modifié!
1 |
_create: function () { |
Variabiliser le sélecteur, présent dans le DOM, sur lequel est initialisé le widget. Remarquer ici le chemin this.options.attributeSelectorId
pour exploiter la valeur de l’option simple attributeSelectorId
déclarée plus haut dans notre objet options
.
Et variabiliser this
(ici sous l’alias that
) est important dès lors qu’on cherche à exécuter une fonction de ce widget (on le verra plus bas):
1 2 |
const $_THIS = $(this.options.attributeSelectorId), that = this; |
On précède chaque exécution d’une fonction de ce widget par notre « that » alias de « this »:
1 |
that.appendCarouselToDOM($_THIS, that.ulToCarousel(that.clonedSelectToUl(that.cloneSelect($_THIS)))); |
Commentaires directement dans le bout de code 😉
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$('.slick-slide').on('click', function () { const $_THIS = $(this); // Les mises à jour des valeurs de nos variables globales se font sous ce mode: ATTRIBUTE_SELECTOR_ELEMENT_ID = $_THIS.closest('ul').attr('id'); SELECTED_OPTION_INDEX = $_THIS.attr('data-slick-index'); // Systématiquement, on précède chaque exécution d'une fonction de ce widget par notre "that" alias de "this": // (si on oublie le "that", la fonction n'est pas exécutée) that.synchronizeCarouselWithSelect(ATTRIBUTE_SELECTOR_ELEMENT_ID, SELECTED_OPTION_INDEX); that.handleCarouselSelectedOptionClass(ATTRIBUTE_SELECTOR_ELEMENT_ID, SELECTED_OPTION_INDEX); }); }, // <= ATTENTION A NE PAS OUBLIER LA VIRGULE JUSTE LA! |
Manière de déclarer une fonction (<nomDeLaFontion>: function(<argument>, <argument>) { return <quelque chose> })
:
1 |
ulToCarousel: function(clonedSelectToUl) { |
Ici, on déclare le chemin this.options.slick
pour exploiter la valeur d’une option du sous-objet « slick » déclaré plus haut dans notre objet « options ».
Note: il y a probablement plus simple pour passer une série d’arguments que de repointer un à un tous les objets. Je manquais de temps sur le projet en question.
1 2 3 4 5 6 7 8 |
return clonedSelectToUl.slick({ infinite: this.options.slick.infinite, slidesToShow: this.options.slick.slidesToShow, slidesToScroll: this.options.slick.slidesToScroll, prevArrow: this.options.slick.prevArrow, nextArrow: this.options.slick.nextArrow }); }, // <= ATTENTION A NE PAS OUBLIER LA VIRGULE JUSTE LA! |
Manière de déclarer une fonction (
1 2 3 4 5 |
synchronizeSelectWithCarousel: function(relatedElementID, selectedOptionIndex) { $('ul#' + relatedElementID).slick('slickGoTo', parseInt(selectedOptionIndex)); }, // <= SAUF SI ON VIENT DE DECLARER LA TOUTE DERNIERE FONCTION DU WIDGET. }); |
On n’oublie pas de retourner le widget avant de refermer définitivement l’accolade et la parenthèse qui embrassent (c’est bô <3!) le widget:
1 2 |
return $.mage.nomDuWidget; }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
define([ 'jquery', 'jquery-ui-modules/widget', 'Magento_ConfigurableProduct/js/configurable', 'slick' ], function($, configurable) { 'use strict'; let ATTRIBUTE_SELECTOR_ELEMENT_ID, // Peut être un ID de <select> ou de <ul> (carousel) SELECTED_OPTION_INDEX; // Index de l'option sélectionnée par l'utilisateur dans un <select> ou un <ul> (carousel) $.widget('mage.attributeCarousel', { options: { attributeSelectorId: '[id^="attribute"]', slick: { infinite: false, slidesToShow: 3, slidesToScroll: 3, prevArrow: '<button type="button" class="clear slick-prev"><svg viewBox="0 0 100 100" class="icon icon-arrow-back-chevron"><use xlink:href="#icon-arrow-back-chevron"></use></svg></button>', nextArrow: '<button type="button" class="clear slick-next"><svg viewBox="0 0 100 100" class="icon icon-arrow-forward-chevron"><use xlink:href="#icon-arrow-forward-chevron"></use></svg></button>' } }, _create: function () { const $_THIS = $(this.options.attributeSelectorId), that = this; // 1. On clone le <select> // 2. On le transforme en <ul>/<li> // 3. On initialise le carousel sur le markup du <select> cloné et transformé en <ul>/<li> // 4. On injecte le carousel dans le DOM that.appendCarouselToDOM($_THIS, that.ulToCarousel(that.clonedSelectToUl(that.cloneSelect($_THIS)))); // Lorsqu'on change la valeur d'un attribut produit depuis le carousel $('.slick-slide').on('click', function () { const $_THIS = $(this); // Mise à jour de: ATTRIBUTE_SELECTOR_ELEMENT_ID = $_THIS.closest('ul').attr('id'); SELECTED_OPTION_INDEX = $_THIS.attr('data-slick-index'); that.synchronizeCarouselWithSelect(ATTRIBUTE_SELECTOR_ELEMENT_ID, SELECTED_OPTION_INDEX); that.handleCarouselSelectedOptionClass(ATTRIBUTE_SELECTOR_ELEMENT_ID, SELECTED_OPTION_INDEX); }); // Lorsqu'on change la valeur d'un attribut produit depuis le select $_THIS.on('change', function () { // Mise à jour de: ATTRIBUTE_SELECTOR_ELEMENT_ID = $_THIS.attr('id'); SELECTED_OPTION_INDEX = $_THIS.prop('selectedIndex'); that.synchronizeSelectWithCarousel(ATTRIBUTE_SELECTOR_ELEMENT_ID, SELECTED_OPTION_INDEX); that.handleCarouselSelectedOptionClass(ATTRIBUTE_SELECTOR_ELEMENT_ID, SELECTED_OPTION_INDEX); }); }, // 1. On clone le <select>, sous conditions... cloneSelect: function(thisObj) { const $_THIS = thisObj, $_THIS_ID = $_THIS.attr('id'); // Vérifier si le select est [disabled] ou si il a déjà été cloné // [TODO_DEV] Rajouter une condition qui vérifie si les options du select affichent des images. Si false, on ne créera pas de carousel pour ce champ. if ($_THIS.attr('disabled') || $('ul#' + $_THIS_ID).length) { // Si l'un des deux cas est true, on ne fait rien return false; } else { // Sinon return $_THIS.clone().prop('id', $_THIS_ID); } }, // 2. On transforme le <select> cloné en <ul>/<li> clonedSelectToUl: function(clonedSelect) { const $_CLONED_SELECT = $(clonedSelect); return $_CLONED_SELECT.find("option").map(function() { const $_THIS = $(this); return $("<li>").attr("value", $_THIS.attr("value")).text($_THIS.text()).get(); }).appendTo($("<ul>").attr({ id: $_CLONED_SELECT.attr("id"), name: $_CLONED_SELECT.attr("name") })).parent().replaceAll($_CLONED_SELECT); }, // 3. On initialise le carousel (slick) sur le markup du <select> cloné et transformé en <ul>/<li> ulToCarousel: function(clonedSelectToUl) { return clonedSelectToUl.slick({ infinite: this.options.slick.infinite, slidesToShow: this.options.slick.slidesToShow, slidesToScroll: this.options.slick.slidesToScroll, prevArrow: this.options.slick.prevArrow, nextArrow: this.options.slick.nextArrow }); }, // 4. On injecte le carousel dans le DOM appendCarouselToDOM: function(thisObj, carouselElement) { return carouselElement.insertBefore(thisObj); }, // On gère la classe CSS sur l'option sélectionnée par l'utilisateur dans le carousel handleCarouselSelectedOptionClass: function(relatedElementID, selectedOptionIndex) { $('ul#' + relatedElementID).find('.is-selected-option').removeClass('is-selected-option'); $('[data-slick-index="' + selectedOptionIndex + '"]', 'ul#' + relatedElementID).addClass('is-selected-option'); }, // Synchronization slick>select (select2 se gère tout seul) synchronizeCarouselWithSelect: function(relatedElementID, selectedOptionIndex) { const SELECTED_OPTION_VALUE = $('option', 'select#' + relatedElementID).eq(selectedOptionIndex).attr('value'); $('select#' + relatedElementID).val(SELECTED_OPTION_VALUE).trigger('change'); }, // synchronization select>slick (select2 se gère toujours encore tout seul :)) synchronizeSelectWithCarousel: function(relatedElementID, selectedOptionIndex) { $('ul#' + relatedElementID).slick('slickGoTo', parseInt(selectedOptionIndex)); // https://github.com/kenwheeler/slick/issues/235#issuecomment-43406956 $(window).trigger('resize'); } }); return $.mage.attributeCarousel; }); // https://www.siphor.com/different-ways-using-javascript-magento-2/ // https://jason.codes/2019/06/magento-2-create-jquery-ui-widget/ |
Fichier app/design/frontend/MyVendor/mytheme/requirejs-config.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var config = { map: { "*": { attributeCarousel: 'js/attribute-carousel' } }, // Paths defines associations from library name (used to include the library, // for example when using "define") and the library file path. paths: { 'slick': 'js/vendor/slick/slick.min' }, // Shim: when you're loading your dependencies, requirejs loads them all // concurrently. You need to set up a shim to tell requirejs that the library // (e.g. a jQuery plugin) depends on another already being loaded (e.g. depends // on jQuery). shim: { 'slick': { deps: ['jquery'] } } }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<script type="text/x-magento-init"> { "#product_addtocart_form": { "attributeCarousel": { "attributeSelectorId": "#attribute76", "slick": { "slidesToShow": 4, "slidesToScroll": 4 } } } } </script> |
Testé fonctionnel Magento 2.4!
Nous souhaitons que la navigation du compte soit visible depuis n’importe quelle page du site, et pas uniquement dans les pages du compte client (à condition d’être connecté à un compte, bien-sûr ;)).
Le <block [...] name="sidebar.main.account_nav" [...] />
qui initie l’affichage du menu du compte client est, out-of-the-box, déclaré dans la pile des layout XML dans le fichier /vendor/magento/module-customer/view/frontend/layout/customer_account.xml. Le code complet, qui contient uniquement les entrées liées au module Customer, est le suivant:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<block class="Magento\Framework\View\Element\Template" name="sidebar.main.account_nav" template="Magento_Theme::html/collapsible.phtml" before="-"> <arguments> <argument name="block_css" xsi:type="string">account-nav</argument> </arguments> <block class="Magento\Customer\Block\Account\Navigation" name="customer_account_navigation" before="-"> <arguments> <argument name="css_class" xsi:type="string">nav items</argument> </arguments> <block class="Magento\Customer\Block\Account\SortLinkInterface" name="customer-account-navigation-account-link"> <arguments> <argument name="label" xsi:type="string" translate="true">My Account</argument> <argument name="path" xsi:type="string">customer/account</argument> <argument name="sortOrder" xsi:type="number">250</argument> </arguments> </block> <block class="Magento\Customer\Block\Account\Delimiter" name="customer-account-navigation-delimiter-1" template="Magento_Customer::account/navigation-delimiter.phtml"> <arguments> <argument name="sortOrder" xsi:type="number">200</argument> </arguments> </block> <block class="Magento\Customer\Block\Account\SortLinkInterface" name="customer-account-navigation-address-link"> <arguments> <argument name="label" xsi:type="string" translate="true">Address Book</argument> <argument name="path" xsi:type="string">customer/address</argument> <argument name="sortOrder" xsi:type="number">190</argument> </arguments> </block> <block class="Magento\Customer\Block\Account\SortLinkInterface" name="customer-account-navigation-account-edit-link"> <arguments> <argument name="label" xsi:type="string" translate="true">Account Information</argument> <argument name="path" xsi:type="string">customer/account/edit</argument> <argument name="sortOrder" xsi:type="number">180</argument> </arguments> </block> <block class="Magento\Customer\Block\Account\Delimiter" name="customer-account-navigation-delimiter-2" template="Magento_Customer::account/navigation-delimiter.phtml"> <arguments> <argument name="sortOrder" xsi:type="number">130</argument> </arguments> </block> </block> </block> |
sidebar.main.account_nav
à la hauteur du thèmePour rappel: nous souhaitons que la navigation du compte soit visible depuis n’importe quelle page du site, et pas uniquement dans les pages du compte client.
Nous allons pour ce faire déplacer la déclaration du block
sidebar.main.account_nav
à la hauteur de notre thème.
Dans votre thème (et si il n’existe pas déjà), créer le fichier app/design/frontend/MyVendor/mytheme/Magento_Theme/layout/default.xml
et copiez/collez-y l’intégralité du block
sidebar.main.account_nav
. Dans l’exemple ci-dessous, je l’ai affecté au container
after.body.start
, mais vous pouvez l’affecter à un autre container, y-compris un container custom que vous auriez créer vous-même pour les besoins spécifiques à votre mise en page.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="after.body.start"> <!-- navigation du compte client --> <block class="Magento\Framework\View\Element\Template" name="sidebar.main.account_nav" template="Magento_Theme::html/collapsible.phtml" before="-"> <arguments> <argument name="block_css" xsi:type="string">account-nav</argument> </arguments> <block class="Magento\Customer\Block\Account\Navigation" name="customer_account_navigation" before="-"> <arguments> <argument name="css_class" xsi:type="string">nav items</argument> </arguments> <block class="Magento\Customer\Block\Account\SortLinkInterface" name="customer-account-navigation-account-link"> <arguments> <argument name="label" xsi:type="string" translate="true">My Account</argument> <argument name="path" xsi:type="string">customer/account</argument> <argument name="sortOrder" xsi:type="number">250</argument> </arguments> </block> <block class="Magento\Customer\Block\Account\Delimiter" name="customer-account-navigation-delimiter-1" template="Magento_Customer::account/navigation-delimiter.phtml"> <arguments> <argument name="sortOrder" xsi:type="number">200</argument> </arguments> </block> <block class="Magento\Customer\Block\Account\SortLinkInterface" name="customer-account-navigation-address-link"> <arguments> <argument name="label" xsi:type="string" translate="true">Address Book</argument> <argument name="path" xsi:type="string">customer/address</argument> <argument name="sortOrder" xsi:type="number">190</argument> </arguments> </block> <block class="Magento\Customer\Block\Account\SortLinkInterface" name="customer-account-navigation-account-edit-link"> <arguments> <argument name="label" xsi:type="string" translate="true">Account Information</argument> <argument name="path" xsi:type="string">customer/account/edit</argument> <argument name="sortOrder" xsi:type="number">180</argument> </arguments> </block> <block class="Magento\Customer\Block\Account\Delimiter" name="customer-account-navigation-delimiter-2" template="Magento_Customer::account/navigation-delimiter.phtml"> <arguments> <argument name="sortOrder" xsi:type="number">130</argument> </arguments> </block> </block> </block> <!-- fin: navigation du compte client --> </referenceContainer> </body> </page> |
sidebar.main.account_nav"
vers notre thèmeAttention: si on compile nos XMLs en l’état, nous allons avoir un conflit entre le layout du module Customer du core de Magento 2 et le layout default.xml
de notre thème, car <block class="Magento\Framework\View\Element\Template" name="sidebar.main.account_nav"
est déclaré deux fois!
Dans votre thème (et si il n’existe pas déjà), créer le fichier app/design/frontend/MyVendor/mytheme/Magento_Customer/layout/customer_account.xml
et y placer le code suivant:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd" label="Customer My Account (All Pages)" design_abstraction="custom"> <head> <title>My Account</title> </head> <body> <attribute name="class" value="account"/> <move element="sidebar.main.account_nav" destination="sidebar.main" after="-" /> <referenceContainer name="sidebar.main"></referenceContainer> </body> </page> |
Ici, le <move element="sidebar.main.account_nav" destination="sidebar.main" after="-" />
permet d’indiquer à Magento 2 que la déclaration initiale du block sidebar.main.account_nav"
a été déplacée du module Customer du core au module Theme de notre thème.
1 |
magerun2 cache:clean layout |
Le menu du compte devrait désormais (et à condition d’être connecté à un compte client) s’afficher dans n’importe quelle page de votre site, à l’endroit indiqué dans le layout XML default.xml
de votre thème.
Comme indiqué en début de billet, l’affichage est probablement plus qu’àpproximatif et c’est à vous de jouer côté LESS, PHTML et JS pour adapter tout ça 🙂
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é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
.
Dans le fichier app/design/frontend/MyVendor/mytheme/requirejs-config.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var config = { // Paths defines associations from library name (used to include the library, // for example when using "define") and the library file path. paths: { 'slick': 'js/vendor/slick/slick.min }, // Shim: when you're loading your dependencies, requirejs loads them all // concurrently. You need to set up a shim to tell requirejs that the library // (e.g. a jQuery plugin) depends on another already being loaded (e.g. depends // on jQuery). shim: { 'slick': { deps: ['jquery'] } } }; |
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:
1 2 3 4 5 6 7 |
define(['jquery','slick'], function($) { return function(config, element) { $(element).slick(config); }; }); |
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.
1 2 3 4 5 6 7 8 9 10 11 12 |
define(['jquery','slick'], function($) { return function(config, element) { let defaults = { prevArrow: '<button type="button" class="btn-round slick-prev"><svg viewBox="0 0 100 100" class="icon icon-arrow-back-chevron"><use xlink:href="#icon-arrow-back-chevron"></use></svg></button>', nextArrow: '<button type="button" class="btn-round slick-next"><svg viewBox="0 0 100 100" class="icon icon-arrow-forward-chevron"><use xlink:href="#icon-arrow-forward-chevron"></use></svg></button>' }; $(element).slick($.extend(defaults, config)); }; }); |
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.
x-magento-init
depuis un fichier PHTMLIci, 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<div id="home-slider"> <div><a href="#">Slide one</a></div> <div><a href="#">Slide two</a></div> <div><a href="#">Slide three</a></div> <div><a href="#">Slide four</a></div> <div><a href="#">Slide five</a></div> <div><a href="#">Slide six</a></div> </div> <script type="text/x-magento-init"> { "#home-slider": { "js/init-slick": {} } } </script> |
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.
1 2 3 4 5 6 7 8 9 10 |
<script type="text/x-magento-init"> { "#home-slider": { "js/init-slick": { "slidesToShow": "4", // # of slides to show at a time "slidesToScroll": "1" // # of slides to scroll at a time } } } </script> |
Testé fonctionnel Magento 2.4 CE. Source: Magento 2: Call customer login page in other phtml.
1 2 |
<?php echo $this->getLayout()->createBlock("Magento\Customer\Block\Form\Login")->setTemplate("Magento_Customer::form/login.phtml")->toHtml();?> <?php echo $this->getLayout()->createBlock("Magento\Customer\Block\Form\Login\Info")->setTemplate("Magento_Customer::newcustomer.phtml")->toHtml(); ?> |