page
--------------------
| container |
| ---------------|
| | |
| | |
1 |
margin-left: calc(50% - (1920px / 2)); |
Le bloc-notes professionnel d'un développeur front-end senior
page
--------------------
| container |
| ---------------|
| | |
| | |
1 |
margin-left: calc(50% - (1920px / 2)); |
Note: ce plugin utilise un autre plugin jQuery, à savoir Sly.
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 |
(function ($) { 'use strict'; $.fn.collectionSlider = function (params) { // Alias du plugin: "collectionSlider". return this.each(function () { // Utiliser each() au cas où le plugin doit s'initialiser plusieurs fois dans la page. let element = $(this), sliderClass = '.ow-slider', sly = {}, [...] timerReload = 0, timerHeight = 0; init(); // IMPORTANT: appeler la fonction d'init() à la racine du return! // Fonctions: function init() { [...] } function initScroller() { sly = new Sly(scrollerWrapper, { horizontal: 1, itemNav: 'basic', smart: 1, mouseDragging: 1, touchDragging: 1, releaseSwing: 1, swingSpeed: 0.2, activateMiddle: 1, activateOn: 'focus', scrollBy: 0, speed: 0, // elasticBounds: 1, easing: 'easeInOutQuint', scrollBar: scrollbar, prevPage: btnPrev, nextPage: btnNext, dragHandle: 1, dynamicHandle: 1 }); sly.on('load', function () { setTimeout(function () { sly.set('speed', 1500); }, 500); }); sly.init(); } // Autres fonctions à la suite... }); }; })(jQuery); |
Exemple avec des paramètres:
1 2 3 4 5 6 7 8 9 10 11 12 |
(function($) { 'use strict'; $.fn.initOwMod13 = function(params) { var options = $.extend({ flag: true, gridMode: false, mainProductSku: '', loadMoreMobile: false, }, params); return this.each(function () { |
Importance de shim
pour déclarer les différentes dépendances du plugin.
1 2 3 4 5 6 7 8 9 10 |
var config = { paths: { collectionSlider: 'Reflet_Catalog/js/collection-slider' }, shim: { 'collectionSlider': { deps: ['jquery', 'mage/translate', 'sly', 'matchHeight'] } } }; |
Ici, veiller à bien re-lister toutes les dépendances à votre plugin (jQuery, sly, …)!
1 2 3 4 5 6 7 8 9 10 11 |
<script type="text/javascript"> require([ 'jquery', 'collectionSlider', 'sly' ], function ($) { 'use strict'; $('.ow-collection__content').collectionSlider(); }); </script> |
Exemple avec des paramètres:
1 2 3 4 5 6 7 8 9 |
<script type="text/javascript"> require([ 'jquery', 'owMod13' ], function ($) { 'use strict'; $('#<?= $moduleId ?>').initOwMod13({flag: true, gridMode:false, mainProductSku: '<?= $defaultSku; ?>'}); }); </script> |
Documentation officielle: Magento 2 Amasty Abandoned Cart Email.
Surcharge dans: app/design/frontend/MyVendor/default/Amasty_Acart/email/template.html
Se rendre dans Stores > Settings > Configuration.
Puis dans Amasty Extensions > Abandoned Cart Email.
Dans la section Testing, passer le champ Safe Mode à Enabled et renseigner votre mail de test.
Puis, se rendre dans Marketing > Abandoned Cart Email > Campaigns > Test pour s’envoyer un mail de test.
Source: Check if a user has scrolled to the bottom (not just the window, but any element)
1 2 3 4 5 |
$(window).scroll(function() { if($(window).scrollTop() + $(window).height() == $(document).height()) { alert("bottom!"); } }); |
1 2 3 4 5 |
$(window).scroll(function() { if($(window).scrollTop() + $(window).height() > $(document).height() - 100) { alert("near bottom!"); } }); |
Deux façons de manipuler des cookies dans Magento 2, en fonction de la nature de votre fichier JS:
1 2 3 4 5 6 7 8 9 |
define([ 'jquery', 'mage/cookies' ], function ($) { 'use strict'; [...] }); |
Récupère la valeur du cookie (et la teste):
1 2 3 4 5 |
var myCookie = $.cookieStorage.get('my-cookie-name'); if (myCookie !== undefined && myCookie === 'on') { return; } |
Affecte une valeur à un cookie:
1 2 |
let myValue = "toto"; $.cookieStorage.set('my-cookie-name', JSON.stringify(myValue)); |
Structure du module:
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 |
/** * /!\ DO NOT USE DEFINE/REQUIRE HERE /!\ */ (function ($) { 'use strict'; window.myModuleName = function (params, callback = null) { let retry = false, retry2 = false, [...] }, params), let _isTouchDevice = function () { [...] }; [...] //Startup $(function () { _initEvents(); _onResize(); }); } })(jQuery); |
Manipulation d’un cookie:
1 2 3 4 5 |
let myCookie = _readMyCookie("my-cookie-name"); if (myCookie !== undefined && myCookie === 'on') { $element.remove(); } |
Le module possède sa propre fonction « faite-maison » pour lire le cookie:
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 |
function _readMyCookie(name) { let nameEQ = name + "=", ca = document.cookie.split(';'), val = null; for (let i = 0; i < ca.length; i++) { let c = ca[i]; while (c.charAt(0) === ' ') { c = c.substring(1, c.length); } if (c.indexOf(nameEQ) === 0) { val = c.substring(nameEQ.length, c.length); val = decodeURIComponent(val); val = (_isNormalInteger(val)) ? parseInt(val) : val; } } return val; } function _isNormalInteger(str) { let n = Math.floor(Number(str)); return (n !== Infinity && String(n) === str && n >= 0); } |
De la même manière, pour créer un cookie:
1 2 3 4 5 6 7 8 9 10 11 |
function _createCookie(name, value, lifetime, baseUrl) { let expires = ""; if (lifetime) { let date = new Date(); date.setTime(date.getTime() + lifetime); expires = "; expires=" + date.toUTCString(); } let domain = (baseUrl && baseUrl !== '') ? ";domain=" + baseUrl : ''; document.cookie = name + "=" + encodeURIComponent(value) + expires + "; path=/" + domain; } |
Testé fonctionnel Magento 2.4.5. – Télécharger la source.
Source: Magento 2: How to resolve Item with the same ID “ID” already exists error?
Autres pistes: firebearstudio.com – How to fix Magento 2 item with the same ID already exists error
Créer un nouveau dossier RSCoder/DuplicateEntry dans app/code.
1 2 3 4 5 6 |
<?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'RSCoder_DuplicateEntry', __DIR__ ); |
1 2 3 4 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="RSCoder_DuplicateEntry" setup_version="1.0.1"></module> </config> |
1 2 3 4 5 6 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Eav\Model\Entity\Collection\AbstractCollection"> <plugin name="find_duplicate_entry" type="RSCoder\DuplicateEntry\plugin\Collection" sortOrder="20"/> </type> </config> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php /** * Copyright © Magento, Inc. All rights reserved. */ namespace RSCoder\DuplicateEntry\plugin; use Magento\Framework\Data\Collection\EntityFactoryInterface; use Magento\Framework\Option\ArrayInterface; class Collection { /** * @param \Magento\Eav\Model\Entity\Collection\AbstractCollection $subject * @param \Closure $process * @param \Magento\Framework\DataObject $dataObject * @return $this */ public function aroundAddItem(\Magento\Eav\Model\Entity\Collection\AbstractCollection $subject, \Closure $process, \Magento\Framework\DataObject $dataObject) { try{ return $process($dataObject); }catch ( \Exception $e){ return $this; } } } |
1 |
php bin/magento module:enable RSCoder_DuplicateEntry |
1 |
php bin/magento setup:upgrade |
ow-prod-delayed-loading
initProductDelayedLoading
de l’attribut afterRender
qui correspond au libellé utilisé pour initialiser le composant dans app/code/Vendor/PageBuilder/view/adminhtml/web/js/content-type/preview-mixin.js
1 2 3 4 5 6 7 8 9 |
<div class="ow-slider__item pagebuilder-content-type pagebuilder-slide children-min-height" attr="data.main.attributes" event="{ mouseover: onMouseOver, mouseout: onMouseOut }, mouseoverBubble: false"> <render args="getOptions().template"></render> <div class="ow-prod-delayed-loading ow-mod__placeholder ow-mod__placeholder--prod-v" data-bind="html: data.main.html" afterRender="initProductDelayedLoading"></div> </div> |
Pour l’ajout de notre widget Vendor_PageBuilder/js/utils/prod-delayed-loading
en tant que composant:
1 2 3 4 5 6 7 8 9 |
define([ 'Vendor_PageBuilder/js/utils/prod-delayed-loading' ], function (_prodDelayedLoading) { 'use strict'; return function (Component) { Component.prototype.retrieveOptions = wrapper.wrapSuper(Component.prototype.retrieveOptions, function () { [...] } Component.prototype.copy = function () { [...] } Component.prototype... |
…et en toute fin de fichier, l’initialisation du composant:
1 2 3 4 5 6 7 8 9 10 |
/** * Init products delayed loading */ Component.prototype.initProductDelayedLoading = function (element) { _prodDelayedLoading.initProdDelayedLoading($(element)); } return Component; } }); |
Un module façon requireJS pour stocker des fonctions amenées à être utilisées dans plusieurs fichiers:
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 |
define(["jquery"], function ($) { function getProductId (str) { let regex = /entity=["']*[^"' >]+/; let match; try { match = str.match(regex)[0]; } catch (e) { return; } // We aren't concerned if this fails let replaceRegex = /entity=["' ]*/; match = match.replace(replaceRegex, ""); return match; } function setProductId (elem, str) { [...] } function showAllModuleItems (elem) { [...] } return { getProductId: getProductId, setProductId: setProductId, showAllModuleItems: showAllModuleItems }; }); |
Dans ce module, de nombreuses utilisations de la variable widget
qui contient, sous forme de string, les données nécessaires à l’affichage du produit:
1 2 3 4 5 6 7 8 9 10 11 |
{{widget type="Reflet\PageBuilder\Block\Selector\Product" template="Reflet_PageBuilder::widget/product/ecom.phtml" selector="product_popover_3" entity="930" module="39-item" theme="01" prod_classes="ow-prod--m-h ow-prod--t-h ow-prod--l-h ow-prod--l-h--small" is_popover="1" store_id="0" }} |
entity
correspond l’ID du produit dans le catalogue. Je la récupère via une regex dans la fonction getProductId
consignée dans le fichier app/code/Vendor/PageBuilder/view/adminhtml/web/js/utils/prod-all.js.template
correspond à la vue dans laquelle les données vont être affichées, en front comme en admin.
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 128 129 130 131 132 133 134 135 136 |
window.discoveredItems = []; define([ 'jquery', 'mage/translate', "Magento_PageBuilder/js/config", "Vendor_PageBuilder/js/utils/prod-all" ], function ( $, $t, _config, _prodAll ) { $.widget('mage.prodDelayedLoading', { $modalsWrapper: null, $pagebuilderStageWrapper: null, $emptySlot: null, loadedProducts: [], _create: function () { this.$modalsWrapper = $('.modals-wrapper'); this.$pagebuilderStageWrapper = $('.pagebuilder-stage-wrapper'); let widget = this.element[0].innerHTML; if (widget === '') { // Slot is empty this.$emptySlot = $('.ow-prod-delayed-loading:not([data-product-id])', this.$pagebuilderStageWrapper); this._initEvents(this.$emptySlot); } else { // Slot is filled with a product this._initDom(); this._initEvents(this.element); } this._keepDiscoveredItemsVisible(); }, _initDom: function () { let widget = this.element[0].innerHTML; let $item = $(this.element); _prodAll.setProductId($item, widget); }, _initEvents: function (thisElem) { thisElem.on('click', this._loadItem.bind(this)); thisElem.on('click', this._storeDiscoveredItemId.bind(this)); }, _storeDiscoveredItemId: function (e) { let productId = $(e.target).attr('data-product-id'); if ($.inArray(productId, window.discoveredItems) === -1) { window.discoveredItems.push(productId); } }, _showSimilarItem: function (productId) { // Show all occurrences of the same product within the page let $item = $('.ow-prod-delayed-loading[data-product-id="' + productId + '"]:not(._loaded)', this.$pagebuilderStageWrapper); $item.click(); }, _keepDiscoveredItemsVisible: function () { // Keep already discovered products visible within the module, // after a user action (move left/right, duplicate...) let i; for (i = 0; i < window.discoveredItems.length; ++i) { let $item = $('.ow-prod-delayed-loading[data-product-id="' + window.discoveredItems[i] + '"]:not(._loaded)', this.$pagebuilderStageWrapper); $item.click(); } }, _loadItem: function (e) { let $item = $(e.target); let _self = this; if ($item.hasClass('_loaded') || $item.hasClass('_loading') || $item.html().indexOf('{{widget') === -1) { return; } $item.find('div').remove(); let widget = $.trim($item.html()); widget = widget.replace('}}', ' prod_is_backend="1"}}'); if (this.loadedProducts[widget]) { $item.addClass('_loaded').html(this.loadedProducts[widget]); $item.removeClass('ow-mod__placeholder ow-mod__placeholder--prod-h ow-mod__placeholder--prod-v'); return; } $item.addClass('_loading').append('<div class="omega-bg-loader"></div>'); $.ajax( _config.getConfig("preview_url"), { method: "POST", data: { role: 'product', directive: widget } } ).done(function (response) { $item.removeClass('_loading ow-mod__placeholder ow-mod__placeholder--prod-h ow-mod__placeholder--prod-v'); if (typeof response.data !== "object" || !Boolean(response.data.content)) { $item.prepend('<div class="message">' + $t("An unknown error occurred. Please try again.") + '</div>'); return; } if (response.data.error) { $item.prepend('<div class="message">' + response.data.error + '</div>'); } else { _self.loadedProducts[widget] = response.data.content; $item.html(response.data.content).addClass('_loaded'); } _self._showSimilarItem(_prodAll.getProductId(widget)); }).fail(function () { $item.removeClass('_loading'); $item.prepend('<div class="message">' + $t("An unknown error occurred. Please try again.") + '</div>'); }); } }); /** * Init product delayedLoading * @param {object} $delayedLoading * @returns void */ function initProdDelayedLoading($delayedLoading) { $delayedLoading.prodDelayedLoading(); } return { initProdDelayedLoading: initProdDelayedLoading }; }); |
1 |
define(["Reflet_PageBuilder/js/utils/prod-all"], function (_prodAll) { |
Ajout d’une fonctionnalité « Show All » dans les options du module:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
_proto.retrieveOptions = function retrieveOptions() { var options = _previewCollection2.prototype.retrieveOptions.call(this); [...] // autres fonctionnalités options.showAll = new _option({ preview: this, icon: "<i class='icon-pagebuilder-show'></i>", title: (0, _translate)("Show All"), action: this.showAll, classes: ["show-all"], sort: 45 }); return options; }; |
Plus bas, définition de la fonction showAll
(noter comment l’habituel $
de jQuery est remplacé par (0, _jquery)
):
1 2 3 4 5 6 7 8 |
/** * */ ; _proto.showAll = function showAll() { _prodAll.showAllModuleItems((0, _jquery)('#'+this.contentType.id)); } |
1 2 3 4 5 |
define([ "Vendor_PageBuilder/js/utils/prod-all" ], function ( _prodAll ) { |
_events.on("<em><nom_du_module></em>:renderAfter", function (args) {
correspond à différents événements (:renderAfter
, :updateAfter
, …) qui se déclenchent au chargement du module en BO.
Les objets contentType
(cf. bout de code suivant) et args
contiennent également pas mal d’infos exploitables.
1 2 3 4 5 |
_proto.bindEvents = function bindEvents() { [...] _events.on("module_13:renderAfter", function (args) { [...] _events.on("module_13:" + _this2.contentType.parentContentType.id + ":updateAfter", function (args) { |
afterObservablesUpdated
: le code qui s’exécute après que l’utilisateur ait cliqué sur le bouton « Save » (j’ai par exemple ajouté l’exécution d’une fonction setProductId
qui ajoute un data-attribute
dans le DOM, avec pour valeur un ID de produit):
this.contentType.id
me donne l’ID du produit sur lequel j’ai itéré en sauvegardant.
1 2 3 4 5 6 7 8 9 |
_proto.afterObservablesUpdated = function afterObservablesUpdated() { [...] // Show uploaded product on "Save" let $item = (0, _jquery)('.ow-prod-delayed-loading', (0, _jquery)('#'+this.contentType.id)); if($item.length) { _prodAll.setProductId($item, $item.html()); $item.click(); } |
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 |
<?php declare(strict_types=1); /** @var $block \Vendor\PageBuilder\Block\Selector\Product */ /** @var $escaper \Magento\Framework\Escaper */ $product = $block->getProduct(); $module = $block->getModule(); $theme = $block->getTheme(); ?> <?php if ($product !== null && $product->getId()) : ?> <?php $imageAlt = $block->getProductAlt($product); $displayedName = $block->getProductDisplayedName($product); /** base64 placeholder */ $placeholder = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAhCAQAAABpC6MWAAAAHUlEQVR42mP8/5+BJMA4qmFUw6iGUQ2jGkY1YAEABt1B4Az9vjkAAAAASUVORK5CYII='; if ($block->isSunglasses($product)) { $placeholder = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAASCAQAAADr5dYVAAAAGElEQVR42mP8/5+BJMA4qmFUw6gG8jUAACa8I++mqcMDAAAAAElFTkSuQmCC'; } ?> <?php if ($module == '03') : ?> <?php /* MODULE 03 */ ?> [...] <?php elseif ($module == '07') : ?> <?php /* MODULE 07 */ ?> [...] <?php if ($theme == '01') : ?> [...] <?php else : ?> [...] <?php endif; ?> <?php elseif ($module == '12' && $block->isBackend()) : ?> // module 12 AND admin view ONLY! <img <?= $block->ariaHidden() ? 'aria-hidden="true"' : '' ?> src="<?= (string) $block->getProductImage($product) ?>" alt="<?= (string) $imageAlt ?>" /> <?php elseif ($module == '18-item') : ?> [...] <?php else : ?> <?php /* DEFAULT */ ?> <img <?= $block->ariaHidden() ? 'aria-hidden="true"' : '' ?> class="lazyload lazyautosizes" data-sizes="auto" src="<?= (string) $placeholder; ?>" data-src="<?= (string) $block->getProductImage($product) ?>" alt="<?= (string) $imageAlt ?>" /> <?php if (mb_strlen($displayedName)) : ?> <p class="pm-product-list-name"><?= (string) $displayedName ?></p> <?php endif; ?> <?php endif; ?> <?php endif; ?> |
Source: « Variable » variables in JavaScript
1 2 3 |
var data = "testVariable"; window["temp_" + data] = 123; alert(window["temp_" + data]); |
La demande était d’ajouter, en front et en back, un widget existant sur plusieurs modules spécifiques du PageBuilder de Magento 2/Adobe Commerce.
Relatif au global, modifié:
Les fichiers importants en cas de modification:
Relatif au module, pas modifié:
ATTENTION: lorsque le module comporte plusieurs thèmes, il peut arriver qu’il existe des déclinaisons du fichier module_xx.js, comme par exemple module_xx–01.js, module_xx–02.js, etc… (où 01, 02, sont les numéros des thèmes). Il faut les éditer également comme indiqué ci-dessous pour initialiser notre nouveau module.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
define([ 'jquery', [...] 'prodPopover' ], function ($) { 'use strict'; return function (config, element) { [...] $('.ow-prod-popover', element).each(function () { $(this).mage('prodPopover'); }); }; }); |
ATTENTION: veiller à bien remplacer toutes les occurences de MyVendor
dans MyVendor_PageBuilder/...
dans tous les fichiers XML de cet exemple (module_41.xml et pagebuilder_module_41_form.xml) par le vrai nom de votre module (vous devriez le retrouver déjà utilisé dans le fichier XML en question). Sinon, la console vous remontera une erreur de compilation sur un fichier widget-directive.js:
Refused to execute script from 'http://245.mydomain.com.local/static/version1681995189/adminhtml/SomeVendor/Backend/en_US/MyVendor_PageBuilder/js/mass-converter/product/widget-directive.js' because its MIME type ('text/plain') is not executable, and strict MIME type checking is enabled.
require.js:168
Uncaught Error: Script error for "MyVendor_PageBuilder/js/mass-converter/product/widget-directive"
https://requirejs.org/docs/errors.html#scripterror
at makeError (require.js:168:17)
at HTMLScriptElement.onScriptError (require.js:1738:36)
ATTENTION #2: lorsque le module fonctionne avec une base qu’on remplit ensuite avec des (sous-)items, les ajouts des bouts de code ci-dessous se font plutôt dans un fichier module_xx_item.xml que dans un fichier module_xx.xml.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_PageBuilder:etc/content_type.xsd"> <type name="module_41" label="New 2/4 V3" menu_section="products" component="Magento_PageBuilder/js/content-type" preview_component="MyVendor_PageBuilder/js/content-type/module-41/preview" master_component="Magento_PageBuilder/js/content-type/master-collection" form="pagebuilder_module_41_form" icon="icon-pagebuilder-module-41" sortOrder="50" translate="label"> <parents default_policy="deny"> <parent name="root-container" policy="allow"/> <parent name="row" policy="allow"/> </parents> <children default_policy="deny"/> <appearances> <appearance name="theme_01" default="true" preview_template="MyVendor_PageBuilder/content-type/module-41/theme-01/preview" master_template="MyVendor_PageBuilder/content-type/module-41/theme-01/master" reader="Magento_PageBuilder/js/master-format/read/configurable"> <elements> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<!--popover_1--> <element name="product_popover_1_1"> <attribute name="product_popover_1_1" persistence_mode="read"/> <html name="product_popover_1_1_widget" preview_converter="Magento_PageBuilder/js/converter/attribute/preview/store-id"/> </element> <element name="product_popover_1_2"> <attribute name="product_popover_1_2" persistence_mode="read"/> <html name="product_popover_1_2_widget" preview_converter="Magento_PageBuilder/js/converter/attribute/preview/store-id"/> </element> <element name="product_popover_1_3"> <attribute name="product_popover_1_3" persistence_mode="read"/> <html name="product_popover_1_3_widget" preview_converter="Magento_PageBuilder/js/converter/attribute/preview/store-id"/> </element> <element name="product_popover_1_4"> <attribute name="product_popover_1_4" persistence_mode="read"/> <html name="product_popover_1_4_widget" preview_converter="Magento_PageBuilder/js/converter/attribute/preview/store-id"/> </element> <!--/popover_1--> |
1 2 |
</elements> <converters> |
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 |
<!--popover_1--> <converter component="MyVendor_PageBuilder/js/mass-converter/product/widget-directive" name="widget_directive_product"> <config> <item name="selector_element" value="product_popover_1_1" /> <item name="is_popover" value="true"/> <item name="html_variable" value="product_popover_1_1_widget"/> </config> </converter> <converter component="MyVendor_PageBuilder/js/mass-converter/product/widget-directive" name="widget_directive_product"> <config> <item name="selector_element" value="product_popover_1_2" /> <item name="is_popover" value="true"/> <item name="html_variable" value="product_popover_1_2_widget"/> </config> </converter> <converter component="MyVendor_PageBuilder/js/mass-converter/product/widget-directive" name="widget_directive_product"> <config> <item name="selector_element" value="product_popover_1_3" /> <item name="is_popover" value="true"/> <item name="html_variable" value="product_popover_1_3_widget"/> </config> </converter> <converter component="MyVendor_PageBuilder/js/mass-converter/product/widget-directive" name="widget_directive_product"> <config> <item name="selector_element" value="product_popover_1_4" /> <item name="is_popover" value="true"/> <item name="html_variable" value="product_popover_1_4_widget"/> </config> </converter> <!--/popover_1--> <!--popover_2--> <converter component="MyVendor_PageBuilder/js/mass-converter/product/widget-directive" name="widget_directive_product"> <config> <item name="selector_element" value="product_popover_2_1" /> <item name="is_popover" value="true"/> <item name="html_variable" value="product_popover_2_1_widget"/> </config> </converter> <converter component="MyVendor_PageBuilder/js/mass-converter/product/widget-directive" name="widget_directive_product"> <config> <item name="selector_element" value="product_popover_2_2" /> <item name="is_popover" value="true"/> <item name="html_variable" value="product_popover_2_2_widget"/> </config> </converter> <converter component="MyVendor_PageBuilder/js/mass-converter/product/widget-directive" name="widget_directive_product"> <config> <item name="selector_element" value="product_popover_2_3" /> <item name="is_popover" value="true"/> <item name="html_variable" value="product_popover_2_3_widget"/> </config> </converter> <converter component="MyVendor_PageBuilder/js/mass-converter/product/widget-directive" name="widget_directive_product"> <config> <item name="selector_element" value="product_popover_2_4" /> <item name="is_popover" value="true"/> <item name="html_variable" value="product_popover_2_4_widget"/> </config> </converter> <!--/popover_2--> |
1 2 3 4 5 |
</converters> </appearance> </appearances> </type> </config> |
ATTENTION: lorsque le module fonctionne avec une base qu’on remplit ensuite avec des (sous-)items, les ajouts des bouts de code ci-dessous se font plutôt dans un fichier pagebuilder_module_xx_item_form.xml que dans un fichier pagebuilder_module_xx_form.xml.
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 |
<fieldset name="products_1" sortOrder="26"> <settings> <label translate="true">Products popover 1</label> <collapsible>true</collapsible> <opened>false</opened> </settings> <urlInput name="product_popover_1_1" sortOrder="5"> <settings> <dataType>text</dataType> <label translate="true">Product 1</label> <notice translate="true">Enter Product's SKU</notice> <dataScope>product_popover_1_1</dataScope> <urlTypes class="productLinksConfigProvider"/> <typeSelectorTemplate>MyVendor_PageBuilder/form/element/urlInput/typeSelector</typeSelectorTemplate> <isDisplayAdditionalSettings>false</isDisplayAdditionalSettings> </settings> </urlInput> <urlInput name="product_popover_1_2" sortOrder="10"> <settings> <dataType>text</dataType> <label translate="true">Product 2</label> <notice translate="true">Enter Product's SKU</notice> <dataScope>product_popover_1_2</dataScope> <urlTypes class="productLinksConfigProvider"/> <typeSelectorTemplate>MyVendor_PageBuilder/form/element/urlInput/typeSelector</typeSelectorTemplate> <isDisplayAdditionalSettings>false</isDisplayAdditionalSettings> </settings> </urlInput> <urlInput name="product_popover_1_3" sortOrder="15"> <settings> <dataType>text</dataType> <label translate="true">Product 3</label> <notice translate="true">Enter Product's SKU</notice> <dataScope>product_popover_1_3</dataScope> <urlTypes class="productLinksConfigProvider"/> <typeSelectorTemplate>MyVendor_PageBuilder/form/element/urlInput/typeSelector</typeSelectorTemplate> <isDisplayAdditionalSettings>false</isDisplayAdditionalSettings> </settings> </urlInput> <urlInput name="product_popover_1_4" sortOrder="20"> <settings> <dataType>text</dataType> <label translate="true">Product 4</label> <notice translate="true">Enter Product's SKU</notice> <dataScope>product_popover_1_4</dataScope> <urlTypes class="productLinksConfigProvider"/> <typeSelectorTemplate>MyVendor_PageBuilder/form/element/urlInput/typeSelector</typeSelectorTemplate> <isDisplayAdditionalSettings>false</isDisplayAdditionalSettings> </settings> </urlInput> </fieldset> |
1 2 3 4 5 6 7 8 |
<fieldset name="products_2" sortOrder="31" component="Magento_PageBuilder/js/form/element/dependent-fieldset"> <settings> [...] <imports> <link name="setDisabledValues">ns = ${ $.ns }, index = product_popover_2_1:disabledValues</link> <link name="setAppearance">ns = ${ $.ns }, index = appearance:value</link> </imports> </settings> |
ATTENTION: à la valeur de l’attribut name
dans <item name="1" xsi:type="string">theme_03</item>
! Il est possible qu’elle ait déjà été défini pour theme_03
plus haut dans le fichier et qu’elle soit différente de 1
. Il faut que, pour un thème donné, la valeur de l’attribut name
soit identique à chaque déclaration.
1 2 3 4 5 6 7 8 |
<urlInput name="product_popover_2_1" sortOrder="5"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="disabledValues" xsi:type="array"> <item name="1" xsi:type="string">theme_03</item> </item> </item> </argument> |
1 2 3 4 5 6 7 |
<settings> [...] <imports> <link name="setDisabled">ns = ${ $.ns }, index = appearance:value</link> </imports> </settings> </urlInput> |
Exemple complet:
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 |
<fieldset name="products_2" sortOrder="31" component="Magento_PageBuilder/js/form/element/dependent-fieldset"> <settings> <label translate="true">Products popover 2</label> <collapsible>true</collapsible> <opened>false</opened> <imports> <link name="setDisabledValues">ns = ${ $.ns }, index = product_popover_2_1:disabledValues</link> <link name="setAppearance">ns = ${ $.ns }, index = appearance:value</link> </imports> </settings> <urlInput name="product_popover_2_1" sortOrder="5"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="disabledValues" xsi:type="array"> <item name="1" xsi:type="string">theme_03</item> </item> </item> </argument> <settings> <dataType>text</dataType> <label translate="true">Product 1</label> <notice translate="true">Enter Product's SKU</notice> <dataScope>product_popover_2_1</dataScope> <urlTypes class="productLinksConfigProvider"/> <typeSelectorTemplate>MyVendor_PageBuilder/form/element/urlInput/typeSelector</typeSelectorTemplate> <isDisplayAdditionalSettings>false</isDisplayAdditionalSettings> <imports> <link name="setDisabled">ns = ${ $.ns }, index = appearance:value</link> </imports> </settings> </urlInput> <urlInput name="product_popover_2_2" sortOrder="10"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="disabledValues" xsi:type="array"> <item name="1" xsi:type="string">theme_03</item> </item> </item> </argument> <settings> <dataType>text</dataType> <label translate="true">Product 2</label> <notice translate="true">Enter Product's SKU</notice> <dataScope>product_popover_2_2</dataScope> <urlTypes class="productLinksConfigProvider"/> <typeSelectorTemplate>MyVendor_PageBuilder/form/element/urlInput/typeSelector</typeSelectorTemplate> <isDisplayAdditionalSettings>false</isDisplayAdditionalSettings> <imports> <link name="setDisabled">ns = ${ $.ns }, index = appearance:value</link> </imports> </settings> </urlInput> <urlInput name="product_popover_3_3" sortOrder="15"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="disabledValues" xsi:type="array"> <item name="1" xsi:type="string">theme_03</item> </item> </item> </argument> <settings> <dataType>text</dataType> <label translate="true">Product 3</label> <notice translate="true">Enter Product's SKU</notice> <dataScope>product_popover_3_3</dataScope> <urlTypes class="productLinksConfigProvider"/> <typeSelectorTemplate>MyVendor_PageBuilder/form/element/urlInput/typeSelector</typeSelectorTemplate> <isDisplayAdditionalSettings>false</isDisplayAdditionalSettings> <imports> <link name="setDisabled">ns = ${ $.ns }, index = appearance:value</link> </imports> </settings> </urlInput> <urlInput name="product_popover_4_4" sortOrder="20"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="disabledValues" xsi:type="array"> <item name="1" xsi:type="string">theme_03</item> </item> </item> </argument> <settings> <dataType>text</dataType> <label translate="true">Product 4</label> <notice translate="true">Enter Product's SKU</notice> <dataScope>product_popover_4_4</dataScope> <urlTypes class="productLinksConfigProvider"/> <typeSelectorTemplate>MyVendor_PageBuilder/form/element/urlInput/typeSelector</typeSelectorTemplate> <isDisplayAdditionalSettings>false</isDisplayAdditionalSettings> <imports> <link name="setDisabled">ns = ${ $.ns }, index = appearance:value</link> </imports> </settings> </urlInput> </fieldset> |
ATTENTION: un mauvais positionnement dans le template HTML peut bloquer l’enregistrement de la page en BO!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<!-- popover_1 --> <if args="data.product_popover_1_1.html"> <div class="ow-prod-popover" attr="{'data-id': 'popover-1-' + contentType.id}"> <div class="ow-prod-popover__item" attr="data.product_popover_1_1.attributes" data-bind="html: data.product_popover_1_1.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_2.html" attr="data.product_popover_1_2.attributes" data-bind="html: data.product_popover_1_2.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_3.html" attr="data.product_popover_1_3.attributes" data-bind="html: data.product_popover_1_3.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_4.html" attr="data.product_popover_1_4.attributes" data-bind="html: data.product_popover_1_4.html"></div> </div> </if> <!-- /popover_1 --> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<!-- popover_2 --> <if args="data.product_popover_2_1.html"> <div class="ow-prod-popover" attr="{'data-id': 'popover-2-' + contentType.id}"> <div class="ow-prod-popover__item" attr="data.product_popover_2_1.attributes" data-bind="html: data.product_popover_2_1.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_2_2.html" attr="data.product_popover_2_2.attributes" data-bind="html: data.product_popover_2_2.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_2_3.html" attr="data.product_popover_2_3.attributes" data-bind="html: data.product_popover_2_3.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_2_4.html" attr="data.product_popover_2_4.attributes" data-bind="html: data.product_popover_2_4.html"></div> </div> </if> <!-- /popover_2 --> |
Bien que les DIVs soient déclarées dans l’ordre suivant au sein de ow-mod__img
dans le template HTML:
ow-prod-popover
pm-responsive-image-wrapper
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 |
<if args="data.picture.html"> <div class="ow-mod__img"> <!-- popover_1 --> <if args="data.product_popover_1_1.html"> <div class="ow-prod-popover" attr="{'data-id': 'popover-1-' + contentType.id}"> <div class="ow-prod-popover__item" attr="data.product_popover_1_1.attributes" data-bind="html: data.product_popover_1_1.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_2.html" attr="data.product_popover_1_2.attributes" data-bind="html: data.product_popover_1_2.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_3.html" attr="data.product_popover_1_3.attributes" data-bind="html: data.product_popover_1_3.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_4.html" attr="data.product_popover_1_4.attributes" data-bind="html: data.product_popover_1_4.html"></div> </div> </if> <!-- /popover_1 --> <div class="pm-responsive-image-wrapper" attr="data.picture.attributes" data-bind="html: data.picture.html"></div> </div> </if> <ifnot args="data.picture.html"> <div class="ow-mod__img" aria-hidden="true"></div> </ifnot> |
…il arrive que le DOM soit rendu dans l’ordre inverse en front:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<!-- popover_1 --> <if args="data.product_popover_1_1.html"> <div class="ow-prod-popover" afterRender="initProductPopover"> <div class="ow-prod-popover__item" attr="data.product_popover_1_1.attributes" data-bind="html: data.product_popover_1_1.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_2.html" attr="data.product_popover_1_2.attributes" data-bind="html: data.product_popover_1_2.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_3.html" attr="data.product_popover_1_3.attributes" data-bind="html: data.product_popover_1_3.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_4.html" attr="data.product_popover_1_4.attributes" data-bind="html: data.product_popover_1_4.html"></div> </div> </if> <!-- /popover_1 --> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<!-- popover_2 --> <if args="data.product_popover_2_1.html"> <div class="ow-prod-popover" afterRender="initProductPopover"> <div class="ow-prod-popover__item" attr="data.product_popover_2_1.attributes" data-bind="html: data.product_popover_2_1.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_2_2.html" attr="data.product_popover_2_2.attributes" data-bind="html: data.product_popover_2_2.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_2_3.html" attr="data.product_popover_2_3.attributes" data-bind="html: data.product_popover_2_3.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_2_4.html" attr="data.product_popover_2_4.attributes" data-bind="html: data.product_popover_2_4.html"></div> </div> </if> <!-- /popover_2 --> |
Certains modules permettent d’afficher un bouton caption (révélant un court texte) en bas à gauche ou à droite de l’image. Lorsque cette fonctionnalité est présente, il faut essayer de placer notre code HTML relatif au popover immédiatement APRèS celui qui affiche le bouton caption. De cette manière, le bouton du popover s’affichera automatiquement en bas à gauche si le caption est paramètré pour s’afficher en bas à droite (via le code CSS ci-dessous, déjà présent dans les fichiers source app/design/frontend/MyVendor/default/Magento_Theme/web/css/source/widgets/_prod-popover.less et app/design/adminhtml/MyVendor/backend/MyVendor_PageBuilder/web/css/source/content-type/_prod-popover-bo.less).
1 2 3 4 5 6 7 8 |
.pm-module-caption-right + .ow-prod-popover { .ow-prod-popover__toggle, .ow-prod-popover__content { right: auto; left: 15px; } } |
Le bout de code:
1 2 3 4 5 6 7 |
<!-- popover_1 --> <if args="data.product_popover_1_1.html"> <div class="ow-prod-popover one" attr="{'data-id': 'popover-1-' + contentType.id}"> [...] </div> </if> <!-- /popover_1 --> |
…doit se répéter autant de fois qu’il y a de déclinaisons.
Exemple complet ci-dessous uniquement pour la partie front, mais la preview.html admin devra logiquement comporter autant de répétitions que le master.html front:
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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
<div data-admin-reference="SL-02-a" class="ow-carousel__item" attr="data.main.attributes"> <if args="data.video.attributes()['data-video-file-src'] !== '' || data.video.attributes()['data-tencent'] === '1' && data.video.attributes()['data-video-src'] !== '' && data.video.attributes()['data-video-file-src'] === ''"> <!-- Native --> <div class="ow-carousel__item-content ow-mod__vid"> <div class="ow-mod__vid-wrapper ow-mod__vid-wrapper--native" data-type="native" data-defer="1" css="{ 'ow-mod__vid-wrapper--3_4': data.main.attributes()['data-desktop-ratio'] === '75', 'ow-mod__vid-wrapper--16_9-tablet': data.main.attributes()['data-tablet-ratio'] === '56.25', 'ow-mod__vid-wrapper--3_4-tablet': data.main.attributes()['data-tablet-ratio'] === '75', 'ow-mod__vid-wrapper--3_4-mobile': data.main.attributes()['data-mobile-ratio'] === '75' }" attr="data.video.attributes"> <div class="ow-mod__vid-overlay"> <div class="ow-mod__vid-overlay-content"> <i class="pm-icon-play" data-bind="style: { color: data.video.attributes()['data-play-button-color']}"></i> </div> <div if="data.picture.html" class="ow-mod__vid-overlay-picture" attr="data.picture.attributes" css="{'ow-mod__vid-overlay-picture--no-opacity': data.video.attributes()['data-overlay-opacity'] === '0'}" data-bind="html: data.picture.html"></div> </div> <video class="ow-mod__vid-player" tabindex="-1" playsinline controlsList="nodownload"> <if args="data.video.attributes()['data-tencent']"> <source data-bind="attr:{src: data.video.attributes()['data-video-src']}" type="video/mp4"/> </if> <ifnot args="data.video.attributes()['data-tencent']"> <source data-bind="attr:{src: data.video.attributes()['data-video-file-src']}" type="video/mp4"/> </ifnot> </video> <p if="data.video_caption.html" class="hidden"> <span>{{widget type = 'MyVendor\PageBuilder\Block\I18n' text = 'Video description: '}}</span> <span attr="data.video_caption.attributes" html="data.video_caption.html"></span> </p> </div> </div> <!-- popover_1 --> <if args="data.product_popover_1_1.html"> <div class="ow-prod-popover one" attr="{'data-id': 'popover-1-' + contentType.id}"> <div class="ow-prod-popover__item" attr="data.product_popover_1_1.attributes" data-bind="html: data.product_popover_1_1.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_2.html" attr="data.product_popover_1_2.attributes" data-bind="html: data.product_popover_1_2.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_3.html" attr="data.product_popover_1_3.attributes" data-bind="html: data.product_popover_1_3.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_4.html" attr="data.product_popover_1_4.attributes" data-bind="html: data.product_popover_1_4.html"></div> </div> </if> <!-- /popover_1 --> </if> <ifnot args="data.video.attributes()['data-video-file-src'] !== '' || data.video.attributes()['data-tencent'] === '1' && data.video.attributes()['data-video-src'] !== '' && data.video.attributes()['data-video-file-src'] === ''"> <if args="data.video.attributes()['data-video-src'] !== ''"> <!-- YouTube/Tencent --> <div class="ow-carousel__item-content ow-mod__vid"> <div class="ow-mod__vid-wrapper ow-mod__vid-wrapper--youtube" data-defer="1" css="{ 'ow-mod__vid-wrapper--3_4': data.main.attributes()['data-desktop-ratio'] === '75', 'ow-mod__vid-wrapper--16_9-tablet': data.main.attributes()['data-tablet-ratio'] === '56.25', 'ow-mod__vid-wrapper--3_4-tablet': data.main.attributes()['data-tablet-ratio'] === '75', 'ow-mod__vid-wrapper--3_4-mobile': data.main.attributes()['data-mobile-ratio'] === '75' }" attr="data.video.attributes"> <div class="ow-mod__vid-overlay"> <div class="ow-mod__vid-overlay-content"> <button class="pm-icon-play" data-bind="style: { color: data.video.attributes()['data-play-button-color']}"> <span class="hidden">{{widget type = 'MyVendor\PageBuilder\Block\I18n' text = 'Launch video'}}</span> </button> </div> <div if="data.picture.html" class="ow-mod__vid-overlay-picture" attr="data.picture.attributes" css="{'ow-mod__vid-overlay-picture--no-opacity': data.video.attributes()['data-overlay-opacity'] === '0'}" data-bind="html: data.picture.html"></div> </div> <div class="ow-mod__vid-player"></div> <p if="data.video_caption.html" class="hidden"> <span>{{widget type = 'MyVendor\PageBuilder\Block\I18n' text = 'Video description: '}}</span> <span attr="data.video_caption.attributes" html="data.video_caption.html"></span> </p> </div> </div> <!-- popover_1 --> <if args="data.product_popover_1_1.html"> <div class="ow-prod-popover two" attr="{'data-id': 'popover-1-' + contentType.id}"> <div class="ow-prod-popover__item" attr="data.product_popover_1_1.attributes" data-bind="html: data.product_popover_1_1.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_2.html" attr="data.product_popover_1_2.attributes" data-bind="html: data.product_popover_1_2.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_3.html" attr="data.product_popover_1_3.attributes" data-bind="html: data.product_popover_1_3.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_4.html" attr="data.product_popover_1_4.attributes" data-bind="html: data.product_popover_1_4.html"></div> </div> </if> <!-- /popover_1 --> </if> <if args="data.video.attributes()['data-video-src'] === ''"> <!-- Img only --> <div class="ow-carousel__item-content"> <a if="data.link.attributes().href" attr="data.link.attributes" class="ow-mod__cta-link"> <if args="data.picture.html"> <div attr="data.picture.attributes" data-bind="html: data.picture.html"></div> </if> <div class="ow-mod__cta" css="{'ow-mod__cta--white': data.link.attributes()['data-color'] === 'white'}"> <div class="pm-font-omega-bold ow-mod__cta-text">{{widget type = 'MyVendor\PageBuilder\Block\I18n' text = 'Discover'}} </div> </div> </a> <ifnot args="data.link.attributes().href"> <if args="data.picture.html"> <div attr="data.picture.attributes" data-bind="html: data.picture.html"></div> </if> </ifnot> <!-- popover_1 --> <if args="data.product_popover_1_1.html"> <div class="ow-prod-popover three" attr="{'data-id': 'popover-1-' + contentType.id}"> <div class="ow-prod-popover__item" attr="data.product_popover_1_1.attributes" data-bind="html: data.product_popover_1_1.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_2.html" attr="data.product_popover_1_2.attributes" data-bind="html: data.product_popover_1_2.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_3.html" attr="data.product_popover_1_3.attributes" data-bind="html: data.product_popover_1_3.html"></div> <div class="ow-prod-popover__item" if="data.product_popover_1_4.html" attr="data.product_popover_1_4.attributes" data-bind="html: data.product_popover_1_4.html"></div> </div> </if> <!-- /popover_1 --> </div> </if> </ifnot> <div if="data.legend.html" class="pm-carousel-legend"> <p class="pm-legend-text pm-font-omega-italic" attr="data.legend.attributes" html="data.legend.html"> </p> </div> </div> |
Objectif: boucler dans l’objet JSON ci-dessous et retourner une liste de toutes les valeurs de la clé example
.
Ressources en ligne:
index
des items de l’objet JSON pour cibler le premier, le dernier, etc… item du tableau: jQuery .each get index of items
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 |
{ "pattern_1": { "example": "AB12 3CD", "pattern": "^[a-zA-Z]{2}[0-9]{2}\\s?[0-9]{1}[a-zA-Z]{2}$" }, "pattern_2": { "example": "A1B 2CD", "pattern": "^[a-zA-Z]{1}[0-9]{1}[a-zA-Z]{1}\\s?[0-9]{1}[a-zA-Z]{2}$" }, "pattern_3": { "example": "AB1 2CD", "pattern": "^[a-zA-Z]{2}[0-9]{1}\\s?[0-9]{1}[a-zA-Z]{2}$" }, "pattern_4": { "example": "AB1C 2DF", "pattern": "^[a-zA-Z]{2}[0-9]{1}[a-zA-Z]{1}\\s?[0-9]{1}[a-zA-Z]{2}$" }, "pattern_5": { "example": "A12 3BC", "pattern": "^[a-zA-Z]{1}[0-9]{2}\\s?[0-9]{1}[a-zA-Z]{2}$" }, "pattern_6": { "example": "A1 2BC", "pattern": "^[a-zA-Z]{1}[0-9]{1}\\s?[0-9]{1}[a-zA-Z]{2}$" } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function processList(data) { var examples = ''; var i = 0; $.each(data, function(index, value) { var length = Object.keys(data).length; if (i === (length -1)) { examples += value.example; } else { examples += value.example + '; '; } i++; }); return examples; } // Result: // AB12 3CD; A1B 2CD; AB1 2CD; AB1C 2DF; A12 3BC; A1 2BC |