D8 Formularios jQuery y Ajax - pierregermain/MyDrupal GitHub Wiki
Páginas relacionadas:
Índice:
- Introducción a JavaScript en Drupal
- Introducción a jQuery
- Librerías jQuery en el núcleo
- Carga condicional de elementos de formulario (#states)
- Ajax en Drupal
- Ajax en enlaces
- Ajax en formularios (Ejemplo)
- Autocompletado de elementos
(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);
- 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
- Creamos nuestro fichero js en la ruta especificada en la librería
(function ($) {
'use strict';
$(document).ready(function() {
$(".fadeout").delay(2000).fadeOut(3000);
});
})(jQuery)
name: Bartik
type: theme
base theme: classy
description: 'A flexible, ...'
package: Core
libraries:
- bartik/global-styling
$build['foo'] = [
'#markup' => $this->t('Lorem ipsum...'),
'#attached' => [
'library' => [
'mymodule/mymodule.bar',
],
],
];
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 %}
/**
* Implements hook_preprocess_HOOK() for maintenance_page.
*/
function seven_preprocess_maintenance_page(&$variables) {
$variables['#attached']['library'][] = 'seven/maintenance-page';
}
/**
* 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';
}
}
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)
UI: /admin/config/development/performance Opción: Aggregate JavaScript files
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: {}
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
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)
http://api.jquery.com/category/selectors/
$("#block1").css("border","3px solid red");
$(".block").css("border","3px solid red");
$("div").css("border","9px solid red");
http://api.jquery.com/attribute-equals-selector/
$('input[value="Monday"]').parent().css("color", "red");
Otros comparadores de atributos:
- http://api.jquery.com/attribute-contains-prefix-selector/
- http://api.jquery.com/attribute-contains-selector/
- http://api.jquery.com/attribute-contains-word-selector/
- http://api.jquery.com/attribute-not-equal-selector/
- http://api.jquery.com/attribute-starts-with-selector/
- http://api.jquery.com/attribute-ends-with-selector/
http://api.jquery.com/all-selector/
- :button. http://api.jquery.com/button-selector/
- :checkbox. http://api.jquery.com/checkbox-selector/
- :file. http://api.jquery.com/file-selector/
- :image. http://api.jquery.com/image-selector/
- :password. http://api.jquery.com/password-selector/
- :radio. http://api.jquery.com/radio-selector/
- :reset. http://api.jquery.com/reset-selector/
- :submit. http://api.jquery.com/submit-selector/
- :text. http://api.jquery.com/text-selector/
- http://api.jquery.com/even-selector/
- http://api.jquery.com/odd-selector/
- http://api.jquery.com/first-selector/
- http://api.jquery.com/last-selector/
$("tr").css("color", "#FFFFFF");
$("tr:even").css("background-color", "red");
$("tr:odd").css("background-color", "green");
$("tr:first").css("background-color", "blue");
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/
http://api.jquery.com/category/events/
$("li").toggle(
function () {
$(this).css("color", "blue");
},
function () {
$(this).css("color", "red");
},
function () {
$(this).css("color", "green");
}
);
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);
});
});
- Definidos en
/core/core.libraries.yml
librearía:
.accordion:
mymodule.accordion:
js:
js/mymodule.js: {}
dependencies:
- core/jquery.ui.accordion
js:
(function ($) {
'use strict';
$(document).ready(function() {
$("#accordion").accordion();
});
})(jQuery)
- 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)
Se encuentran buscando en /core/core.libraries.yml el string jquery.ui.effects.*.
$("#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});
- 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.
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.
- 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);
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'
- 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.
Ver: /core/lib/Drupal/Core/Ajax/*Command.php
Algunos comandos:
- AppendCommand
- RemoveCommand
- AlertCommand
- OpenDialogCommand
- OpenModalDialogCommand
- RedirectCommand
Este ejemplo esta basado en ajax_example del módulo examples (Examples for Developers).
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 puede tener las siguientes opciones
- 'callback'
- 'wrapper'
- 'method'
- 'effect'
- 'speed'
- 'event'
- 'prevent'
- 'progress'
- 'url'
Libro III p.366
Ajax forms en drupal https://www.drupal.org/docs/drupal-apis/javascript-api/ajax-forms