Hay muchas formas de aprender algo, y a cada persona le funcionan mejor distintas opciones: algunos prefieren leer guías o artículos, otros prefieren videos, y otros prefieren ejemplos. A mi me funciona mejor con ejemplos, así que cuando necesito aprender algo nuevo en Drupal, intento recurrir al propio código de Drupal core.
Si necesitas conseguir algo con código y no tienes claro por dónde empezar, piensa: ¿dónde he visto esto yo antes?
Puede que encuentres un ejemplo en tu propio código. Este artículo cubre un caso de uso real que he tenido con Drupal Commerce, simplificado un poco por la brevedad del artículo. La historia de usuario sería algo como: Como cliente, quiero refrescar el carrito en lugar de continuar con la compra después de editar la cantidad de cualquier línea de pedido, para poder ver si hay descuentos mejores que apliquen por cuestiones de volumen de compras.
Para nuestro formulario, esto significa: cuando esté en la página de Carrito, deshabilita el botón de Checkout cuando el usuario edite la cantidad, y habilita el botón de Actualizar carrito.
Pero para una mejor experiencia de usuario, queremos marcar para el usuario qué líneas de pedido ha editado. ¿Dónde hemos visto esto antes? En el formulario de traducción de la interfaz.
Si habilitas el módulo locale, y añades algún otro idioma en tu web, tendrás la página de traducción de interfaz
Así que vamos a mirar el código e inspeccionar cómo hacerlo. Para mí, la mejor forma si no estás familiarizado con ese código es buscar las cadenas de la interfaz afectadas, o la ruta de esa página en mi IDE, e indagar hasta que encuentro cómo funciona.
En este caso, si buscas "Changes made in this table" encuentras core/modules/locale/locale.admin.js. Los archivos JavaScript se incluyen vía libraries, por lo que buscando ese nombre de archivo te chocarás con la biblioteca drupal.locale.admin definida en core/modules/locale/locale.libraries.yml.
Si buscas ahora esa biblioteca, terminas en varios formularios que la incluyen con:
$form['#attached']['library'][] = 'locale/drupal.locale.admin';
Pues esto es todo lo que está involucrado en hacer esta interfaz de traducciones, ahora sólo tienes que imitarlo.
Vamos a crear un módulo. El modo más rápido es con drush, ejecutando
ddev drush generate module-standard
He seleccionado commerce_cart_forcerefresh como nombre. Para el resto de opciones, he seleccionado generar el archivo libraries, que quedará así:
commerce_cart_forcerefresh:
js:
js/commerce-cart-forcerefresh.js: {}
css:
component:
css/commerce-cart-forcerefresh.css: {}
dependencies:
- core/jquery
- core/drupal
- core/drupal.form
- core/jquery.once
En nuestro css:
form#views-form-commerce-cart-form-default-1 tr.changed {
background: #ffb;
}
Y en nuestro JavaScript:
(function ($, Drupal, drupalSettings) {
'use strict';
Drupal.behaviors.cartForceRefresh = {
attach: function (context) {
var $form = $('form#views-form-commerce-cart-form-default-1').once('cartItemDirty');
var $updateSubmit = $('form#views-form-commerce-cart-form-default-1 #edit-submit')
.prop('disabled', true);
if ($form.length) {
$form.one('formUpdated.cartItemDirty', 'table', function () {
var $marker = $(Drupal.theme('cartItemChangedWarning')).hide();
$(this).addClass('changed').before($marker);
$marker.fadeIn('slow');
});
$form.on('formUpdated.cartItemDirty', 'tr', function () {
var $row = $(this);
var marker = Drupal.theme('cartItemChangedMarker');
$row.addClass('changed');
var $updateSubmit = $('form#views-form-commerce-cart-form-default-1 #edit-submit')
.prop('disabled', false);
var $checkoutSubmit = $('form#views-form-commerce-cart-form-default-1 #edit-checkout')
.prop('disabled', true);
if ($row.length) {
$row.find('td:first-child .js-form-item').append(marker);
}
});
}
},
detach: function (context, settings, trigger) {
if (trigger === 'unload') {
var $form = $('form#views-form-commerce-cart-form-default-1').removeOnce('cartItemDirty');
if ($form.length) {
$form.off('formUpdated.cartItemDirty');
}
}
}
};
$.extend(Drupal.theme, {
cartItemChangedMarker: function cartItemChangedMarker() {
return '<abbr class="warning ajax-changed" title="' + Drupal.t('Changed') + '">*</abbr>';
},
cartItemChangedWarning: function cartItemChangedWarning() {
return '<div class="clearfix messages messages--warning">' +
Drupal.theme('cartItemChangedMarker') + ' ' +
Drupal.t('Update the cart for the changes to take effect.') + '</div>';
}
});
})(jQuery, Drupal, drupalSettings);
Ojo que el id del formulario se usa en varias partes de nuestro css y javascript, y que depende del id de la view de Drupal. Tenlo en cuenta si no usas la vista por defecto, si tienes diferentes tipos de pedido, etc. En mi caso real, mi tema ya proveía una clase css en ese formulario que podía usar en lugar del id.
Lo último es asegurar que el behavior se añade a nuestro formulario. La forma más sencilla para esta demo es implementar hook_form_FORMID_alter(), que también depende del id de la vista. Nuestro archivo commerce_cart_forcerefresh.module es:
<?php
use Drupal\Core\Form\FormStateInterface;
/**
* @file
* Primary module hooks for commerce_cart_forcerefresh module.
*/
function commerce_cart_forcerefresh_form_views_form_commerce_cart_form_default_1_alter(&$form, FormStateInterface $form_state, $form_id) {
$form['#attached']['library'][] = 'commerce_cart_forcerefresh/commerce_cart_forcerefresh';
}
Y produce el resultado esperado:
¡Espero que sirva! Todo el código lo tienes en https://github.com/penyaskito/drupal_commerce_cart_forcerefresh
Puedes leer más sobre behaviors de JavaScript en esta guía: JavaScript & Drupal 101 TUTORIAL HANDBOOK TOTAL MAX POWER 2000 (amor al primer behavior), de davidjguru.