D8 Formularios jQuery y Ajax - pierregermain/MyDrupal GitHub Wiki

Páginas relacionadas:

Índice:

Introducción a JavaScript en Drupal

(function () {
  'use strict';
  // Custom javascript
})();

En Drupal:

  • todo código JavaScript debe declararse dentro de una función de cierre (closure)
  • siempre debe utilizarse la directiva 'use strict'
  • Pasando el parámetro $ a esta función podremos usar internamente la función $(), alias de jQuery()
(function ($) {
  'use strict';
  //jQuery code
})(jQuery);

Añadir librería jQuery a nuestro módulo

  1. Creamos nuestro fichero de librerías: mymodule.libraries.yml

En este ejemplo:

  • estamos definiendo una librería js y otra de css
  • le ponemos dependencia hacia jQuery
foo:
  version: 1.x
  css:
    theme:
      css/foo.css: {}
  js:
    js/foo.js: {}
  dependencies:
    - core/jquery
  1. Creamos nuestro fichero js en la ruta especificada en la librería
(function ($) {
    'use strict';
    $(document).ready(function() {
        $(".fadeout").delay(2000).fadeOut(3000);
    });
})(jQuery)

Otras formas de añadir librearía js

Añadir liberías desde los themes

name: Bartik
type: theme
base theme: classy
description: 'A flexible, ...'
package: Core
libraries:
  - bartik/global-styling

Añadir librerías desde el render array

$build['foo'] = [
  '#markup' => $this->t('Lorem ipsum...'),
  '#attached' => [
  'library' => [
    'mymodule/mymodule.bar',
    ],
  ],
];

Añadir librería desde un twig

Se hace con attach_library()('mymodule/mylibrary')

{% block messages %}
  {% if message_list is not empty %}
    {{ attach_library('bartik/messages') }}
    <div class="messages__wrapper layout-container">
      {{ parent() }}
    </div>
  {% endif %}
{% endblock messages %}

Añadir libraría desde un preprocess

/**
* Implements hook_preprocess_HOOK() for maintenance_page.
*/
function seven_preprocess_maintenance_page(&$variables) {
  $variables['#attached']['library'][] = 'seven/maintenance-page';
}

Añadir librearía a ciertas páginas

/**
* Implements hook_preprocess_HOOK() for page.
*/
function mymodule_theming_preprocess_page(&$variables) {
  $variables['page']['#cache']['contexts'][] = 'route';
  if (\Drupal::routeMatch()->getRouteName() === 'mymodule_theming.render_elements') {
    $variables['#attached']['library'][] = 'mymodule_theming/mymodule_theming.css';
  }
}
/**
* Implements hook_page_attachments().
*/
function foo_page_attachments(array &$attachments) {
  $attachments['#attached']['library'][] = 'foo/bar';
  if (!\Drupal::currentUser()->hasPermission('custom permissions')) {
    $attachments['#attached']['library'][] = 'foo/baz';
  }
}

Más info: https://www.drupal.org/docs/8/creating-custom-modules/adding-stylesheets-css-and-javascript-js-to-a-drupal-8-module

Ejemplo completo con una página tipo Controlador

Fichero routing:

mymodule.fadeout:
  path: '/mymodule/jquery/fadeout'
  defaults:
    _controller: '\Drupal\mymodule\Controller\MymoduleJqueryController::fadeout'
  _title: 'Fade out example'
  requirements:
    _permission: 'access content'

Fichero libraries:

mymodule.fadeout:
  js:
    js/mymodule_fadeout.js: {}
  dependencies:
    - core/jquery

Fichero Controlador en /src/Controller/MymoduleJqueryController.php

<?php

namespace Drupal\mymodule\Controller;

use Drupal\Core\Controller\ControllerBase;

class MymoduleJqueryController extends ControllerBase {

  public function fadeout() {
    $build['text'] = [
      '#markup' => '<p>' . $this->t('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis tristique enim lorem, quis imperdiet ante luctus non. Phasellus sapien neque, placerat sed odio ut, efficitur tincidunt dui.') . '</p>',
    ];
    $build['temp_text'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#attributes' => [
        'class' => 'fadeout',
      ], 
      '#value' => $this->t('This text will disappear in 5 seconds...'),
      '#attached' => [
        'library' => [
          'mymodule/mydmoule.fadeout',
        ],
      ],
    ];
    return $build;
  }

}

Fichero js en js/mymodule_fadeout.js

(function ($) {
    'use strict';
    $(document).ready(function() {
        $(".fadeout").delay(2000).fadeOut(3000);
    });
})(jQuery)

Juntar archivos js

UI: /admin/config/development/performance Opción: Aggregate JavaScript files

Orden de ejecución de las librerías

Por defecto, todos los archivos JS se cargan al final del documento (en el pie). Si necesitamos que se cargue en el encabezado, lo indicaremos en la declaración de la librería:

js-header:
  header: true
  js:
    header.js: {}

JavaScript configurable

Añadiendo la dependencia con la librería core/drupalSettings, podemos añadir variables al código JavaScript desde PHP.

cuddly-slider:
  version: 1.x
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery
    - core/drupalSettings

Ahora desde PHP podemos añadir la variable usando:

$build['#attached']['library'][] = 'example_module/cuddly-slider';
$build['#attached']['drupalSettings']['mymodule']['myvariable'] = 'bar';

Desde JS podemos acceder a la variable usando

drupalSettings.mymodule.myvariable

Introducción a jQuery

$(document).ready()

http://api.jquery.com/ready/

Como norma general utilizaremos siempre esta estructura antes de añadir el código jQuery personalizado.

(function ($) {
  'use strict';
  $(document).ready(function() {
    // Código jQuery
  });
})(jQuery)

Selectores

http://api.jquery.com/category/selectors/

$("#block1").css("border","3px solid red");
$(".block").css("border","3px solid red");
$("div").css("border","9px solid red");

Valor de atributo

http://api.jquery.com/attribute-equals-selector/

$('input[value="Monday"]').parent().css("color", "red");

Otros comparadores de atributos:

Todos los elementos

http://api.jquery.com/all-selector/

Tipo de Elemento

Orden de los elementos

$("tr").css("color", "#FFFFFF");
$("tr:even").css("background-color", "red");
$("tr:odd").css("background-color", "green");
$("tr:first").css("background-color", "blue");

Modificación de CSS

Obtiene el valor:

var color = $("div.left").css("background-color");

Aplicar el valor:

$("p").css("color", "#000000");
$("#block1").css({'background-color': '#ffe', 'border':
'5px solid #ccc'});
$("p").addClass("class1 class2");
$("p").removeClass("oldclass1 oldclass2");
$("p").removeClass("oldclass1 oldclass2").addClass("class1");

Más info en:

http://api.jquery.com/category/css/ http://api.jquery.com/category/attributes/

Eventos

http://api.jquery.com/category/events/

$("li").toggle(
function () {
$(this).css("color", "blue");
},
function () {
$(this).css("color", "red");
},
function () {
$(this).css("color", "green");
}
);

Efectos

http://api.jquery.com/category/effects/

$(document).ready(function(){
$("#ocultar").click(function(event){
event.preventDefault();
$('#box1').fadeOut(2000);
});
$("#mostrar").click(function(event){
event.preventDefault();
$("#box1").slideDown(3000);
});
});

Librerías jQuery en el núcleo

  • Definidos en /core/core.libraries.yml

Crear un Accordion

librearía:

.accordion:
mymodule.accordion:
  js:
    js/mymodule.js: {}
  dependencies:
    - core/jquery.ui.accordion

js:

(function ($) {
    'use strict';
    $(document).ready(function() {
        $("#accordion").accordion();
    });
})(jQuery)

Otras librerías que se pueden usar en Drupal

  • Dialog (core/jquery.ui.dialog) Ventana flotante.
  • Drag and Drop:
    • Draggable (core/jquery.ui.draggable)
    • Droppable (core/jquery.ui.droppable)
  • Progressbar (core/jquery.ui.progressbar)
  • Resizable (core/jquery.ui.resizable)
  • Selectable (core/jquery.ui.selectable
  • Sortable (core/jquery.ui.sortable)
  • Tabs (core/jquery.ui.tabs)

Efectos que existen en Drupal

Se encuentran buscando en /core/core.libraries.yml el string jquery.ui.effects.*.

Traducción de cadenas

$("#block1").append(Drupal.t('Advanced search'));
return Drupal.t('@count styles configured', {'@count': count});
var nodesCount = Drupal.formatPlural(count, '1 node', '@count nodes');
var nodesCountOfType = Drupal.formatPlural(count, '1 node of
@type', '@count nodes of @type', {@type: nodeType});

Carga condicional de elementos de formulario (#states)

  • Librería relacionada: core/drupal.states (carga /core/misc/states.js)
  • No hace falta cargar dicha librería (todo es automatico usando la propiedad #states)
  • La propiedad #states asignada a un elemento de formulario, nos permite añadir condiciones de comportamiento de ese elemento con respecto a otros elementos del formulario.

Propiedad #states

Ejemplo: En el siguiente ejemplo, el elemento options solo se mostrará si el elemento checkbox1 ha sido seleccionado (checked).

    $form['options'] = [
      '#type' => 'container',
      '#states' => [
        'visible' => [
          ':input[name="checkbox1"]' => ['checked' => TRUE],
        ],

Ejemplo: En el siguiente ejemplo, el elemento options se mostrará si ambas condiciones se cumplen

    $form['options'] = [
      '#type' => 'container',
      '#states' => [
        'visible' => [
         [':input[name="checkbox1"]' => ['checked' => TRUE]],
         [':input[name="confirmation_type"]' => ['value' => 'inline']]
        ],

Ejemplo: En el siguiente ejemplo, el elemento options se mostrará si una de las dos condiciones se cumplen

    $form['options'] = [
      '#type' => 'container',
      '#states' => [
        'visible' => [
         [':input[name="checkbox1"]' => ['checked' => TRUE]],
           'or',
         [':input[name="confirmation_type"]' => ['value' => 'inline']]
        ],

El estado de un elemento contenedor afecta a todos los elementos que contiene, de forma que se mostrarán u ocultarán de forma conjunta, sin tener que aplicar condiciones de estado a sus elementos individuales.

Estados:

  • enabled / disabled. Elemento activado o desactivado.
  • required / optional. Elemento obligatorio u opcional.
  • visible / invisible. Elemento visible o invisible.
  • checked / unchecked. Elemento seleccionado o no seleccionado.
  • expanded / collapsed. Elemento expandido o cerrado.

Condiciones remotas

  • empty / filled. Si el elemento está vacío o rellenado.
  • checked / unchecked. Si el elemento ha sido seleccionado o no.
  • expanded / collapsed. Si el elemento está expandido o cerrado.
  • value. Valor del elemento.

Ajax en Drupal

Drupal behaviors

  • Con Ajax no debemos usar el método .ready()
  • A través de Drupal behaviors podemos definir comportamientos sobre ciertos elementos de la página, de forma que en cualquier momento puedan volver a ser ejecutados sobre los nuevos elementos añadidos al DOM.
(function ($) {
    'use strict';
    Drupal.behaviors.addRedtext = {
        attach: function (context, settings) {
            $("p", context).addClass("red-text");
        }
    };
    $(document).ready(function () {
        //Añade un nuevo párrafo. Esta etiqueta <p> no recibe la nueva clase
        $("#maintext").append("<p>Vivamos la vida  <p>");
        //Ejecuta los behaviors
        Drupal.attachBehaviors();
    });
})(jQuery);

La función Drupal.attachBehaviors(context, settings) puede recibir los parámetros context y settings.

El parámetro context se utiliza para pasar a la función el elemento que ha sido añadido. Cuando no especificamos un contexto específico, la función utilizará como contexto todo el documento (document).

Cuando no especificamos en settings parámetros de configuración específicos para el contexto actual, se utilizará el objeto global drupalSettings

Se pueden quitar (opcional) y añadir comportamientos:

(function ($) {
    'use strict';
    Drupal.behaviors.exampleBehavior = {
        attach: function (context, settings) {
         // Cuando se añade el contenido a la página
        },
        detach: function (context, settings) {
         // Cuando se elimina el contenido de la página (opcional)
        },
    };
})(jQuery);

Uso de Ajax en Drupal

API: https://api.drupal.org/api/drupal/core!core.api.php/group/ajax/8

Se puede usar Ajax en

  • Formularios usando la porpiedad #ajax
  • Enlaces usando la clase 'use-ajax'
  • Botones de formulario usando la clase 'use-ajax-submit'

Ajax en enlaces

  • al hacer click sobre este, se ejecute una función vía Ajax

Ruta:

mymodule.link:
  path: '/mymodule/ajax/link'
  defaults:
    _controller: '\Drupal\mymodule\Controller\MymoduleAjaxController::link'
    _title: 'Ajax link example'
  requirements:
    _permission: 'access content'
mymodule.link_callback:
  path: '/mymodule/ajax/link-callback'
  defaults:
    _controller: '\Drupal\mymodule\Controller\MymoduleAjaxController::linkCallback'
    _title: 'Ajax link example callback'
  requirements:
    _permission: 'access content'

Controlador

<?php

namespace Drupal\mymodule\Controller;

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Url;

class MymoduleAjaxController extends ControllerBase {

  public function link() {
    $build['text'] = [
      '#markup' => '<p>' . $this->t('Click the link to get the updated time from server.') . '</p>',
    ];
    $build['time'] = [
      '#type' => 'html_tag',
      '#tag' => 'div',
      '#value' => date("H:i:s"),
      '#attributes' => [
        'id' => 'time',
      ],
    ];
    $build['refresh_time'] = [
      '#title' => $this->t('Refresh time'),
      '#type' => 'link',
      '#url' => Url::fromRoute('mymodule.link_callback'),
      '#attributes' => [
        'class' => 'use-ajax',
      ],
    ];
    return $build;
  }

  public function linkCallback() {
    $response = new AjaxResponse();
    $response->addCommand(new ReplaceCommand(
      '#time',
      '<div id="time">' . date("H:i:s") . ' (' . $this->t("via
AJAX") . ')</div>'));
    return $response;
  }

}
  • link_callback() es la función que realiza el callback
  • El comando ReplaceCommand hace el replace del DOM. Busca #time y lo reemplaza.

Comandos Ajax

Ver: /core/lib/Drupal/Core/Ajax/*Command.php

Algunos comandos:

  • AppendCommand
  • RemoveCommand
  • AlertCommand
  • OpenDialogCommand
  • OpenModalDialogCommand
  • RedirectCommand

Ajax en formularios (Ejemplo)

Este ejemplo esta basado en ajax_example del módulo examples (Examples for Developers).

Definición de la actividad

Tenemos dos listas de selección dependientes entre sí, donde el listado de la segunda lista dependerá de la opción seleccionada en la primera. Usamos listas estáticas para simplificar el ejemplo.

Listas:

Temperatura:
cool
hot

Color:
Si cool entonces azul, verde
Si hot entonces red, orange

Definimos el routing del formulario (no hace falta definir el del callback)

forcontu_ajax.form:
  path: '/forcontu/ajax/form'
  defaults:
    _form: '\Drupal\forcontu_ajax\Form\AjaxForm'
    _title: 'Ajax Form Example'
  requirements:
    _permission: 'access content'

Definimos el Formulario:


<?php

namespace Drupal\forcontu_ajax\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

class AjaxForm extends FormBase {

  private $colors = [
    'warm' => [
      'red' => 'Red',
      'orange' => 'Orange',
      'yellow' => 'Yellow',
    ],
    'cool' => [
      'blue' => 'Blue',
      'purple' => 'Purple',
      'green' => 'Green',
    ],
  ];

  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['temperature'] = [
      '#title' => $this->t('Temperature'),
      '#type' => 'select',
      '#options' => ['warm' => 'Warm', 'cool' => 'Cool'],
      '#empty_option' => $this->t('-select'),
      '#ajax' => [
        'callback' => '::colorCallback',
        'wrapper' => 'color-wrapper',
      ],
    ];
    // Desactivar la caché de formulario
    $form_state->setCached(FALSE);
    $form['color_wrapper'] = [
      '#type' => 'container',
      '#attributes' => ['id' => 'color-wrapper'],
    ];
    $form['actions'] = [
      '#type' => 'actions',
    ];
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];
    return $form;
  }

  public function getFormId() {
    return 'forcontu_ajax_ajax_form';
  }

  public function colorCallback(array &$form, FormStateInterface $form_state) {
    $temperature = $form_state->getValue('temperature');
    $form['color_wrapper']['color'] = [
      '#type' => 'select',
      '#title' => $this->t('Color'),
      '#options' => $this->colors[$temperature],
    ];
    return $form['color_wrapper'];
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
  }

}

La llamada callback se realiza desde

'#ajax' => [
  'callback' => '::colorCallback',
  'wrapper' => 'color-wrapper',
],

donde 'wrapper' es el identificador (id) del elemento HTML que actúa como envoltorio del contenido Ajax. Nuestro callback recibe cómo parémetro todo el $form_state de forma automatica

La propiedad #ajax

La propiedad ajax puede tener las siguientes opciones

  • 'callback'
  • 'wrapper'
  • 'method'
  • 'effect'
  • 'speed'
  • 'event'
  • 'prevent'
  • 'progress'
  • 'url'

Autocompletado de elementos

Libro III p.366

Mas info

Ajax forms en drupal https://www.drupal.org/docs/drupal-apis/javascript-api/ajax-forms

⚠️ **GitHub.com Fallback** ⚠️