Cómo activar Drupal.attachBehaviors después de un Ajax exitoso

10

Tengo un módulo que actualiza un nodo a través de ajax cuando se hace clic en un enlace.

El enlace es una palanca, debe actualizar el nodo con el valor 1 en el primer clic, luego con el valor 0 en un clic posterior, etc. Como activar / desactivar algo.

El siguiente código funciona en el primer clic después de cargar la página, pero no en los clics posteriores. Creo que Drupal.attachBehaviors debe llamarse / activarse después de cada clic, pero no puedo entender cómo hacerlo.

  1. El módulo

    function mymodule_menu() {
      $items['mypath/%/%/ajax'] = array(
      'title' => 'My title',
      'page callback' => 'mymodule_ajax_callback',
      'page arguments' => array(1,2),
      'access arguments' => array('access content'),
      'type' => MENU_CALLBACK,
      );
    
      ...
    }
    
    function mymodule_ajax_callback($id, $status) {
      //Validation[...]
    
      //Node Update using $id as the nid and $status as the field value[...]
    
      // Define a new array to hold our AJAX commands.
      $ajax_commands = array();
    
      // Create a new AJAX command that replaces the #div.
      $replacedivid = '#status'.$id;
      $replacestring = '<div id="status'.$id.'"><a data-url="'.base_path().'mypath/'.$id.'/'.$new_status.'/ajax" title="This item is marked as '.$status_text.'" id="statuslink'.$id.'" class="midui">'.$status_text.'</a></div>';
    
    
      $ajax_commands[] = ajax_command_replace($replacedivid, $replacestring);
    
    
      return drupal_json_output($ajax_commands);
    }
    
  2. Javascript

    (function ($) {
      Drupal.behaviors.mymodule = {
        attach: function(context, settings) {
          var $uilink = $('.midui'); //find all links
    
          for (var i=0;i<$uilink.length;i++) { //Loop
            var $link = $('#' + $uilink[i].id);
            if (!$link.hasClass("middone")) {
    
              new Drupal.ajax('#' + $uilink[i].id, $link, {
                url: $link.attr('data-url'),
                effect: 'fade',
                settings: {},
                progress: {
                  type: 'throbber'
                },
                event: 'click tap'
              });
    
              $link.addClass("middone"); //add class when we're done
    
            }
          }
        }
      }
    })(jQuery);
    
  3. Lo que he probado hasta ahora:

(a) Agregue una ajax_command_invoke(NULL, 'mymodule');a la matriz $ ajax_commands junto con una $.fn.mymodulefunción

(b) Agregar $('body').ajaxSuccess(Drupal.attachBehaviors);a mi javascript. ajaxComplete también lo intentó. Lo probé en el documento también.

(c) Cree un comando personalizado como se detalla aquí http://www.jaypan.com/tutorial/calling-function-after-ajax-event-drupal-7

Nota: Sé que es solo una cuestión de activar attachBehaviors después de cada clic para 'ajaxificar' el nuevo html que se inserta / modifica. Si hago clic en el enlace y luego escribo Drupal.attachBehaviors () en la consola, el enlace será procesado nuevamente por mi javascript, como lo demuestra la adición de la clase 'middone', y se puede hacer clic nuevamente.

Nota: También es interesante, si dejo el $ajax_commandsvacío y lo devuelvo (matriz vacía) al final de la función de devolución de llamada, el enlace seguirá siendo cliqueable en el primer clic y en los siguientes. Tendrá la funcionalidad que estoy buscando (una palanca). Sin embargo, dado que no se realiza ningún cambio en el html después de cada clic, el usuario no puede saber si el interruptor está activado o desactivado.

Cualquier sugerencia sería muy apreciada.

================================================== =====

Una respuesta parcial:

La función de éxito de Drupal ajax.js solo vuelve a unir los comportamientos de los formularios (¿creo?)

    if (this.form) {
      var settings = this.settings || Drupal.settings;
      Drupal.attachBehaviors(this.form, settings);
    }

así que decidí hackear la función de éxito de todos mis objetos ajax.

El Javascript ahora se convierte

    (function ($) {
      Drupal.behaviors.mymodule = {
        attach: function(context, settings) {
          var $uilink = $('.midui'); //find all links

          for (var i=0;i<$uilink.length;i++) { //Loop
            var $link = $('#' + $uilink[i].id);
            if (!$link.hasClass("middone")) {

              myAjax = new Drupal.ajax('#' + $uilink[i].id, $link, {
                url: $link.attr('data-url'),
                effect: 'fade',
                settings: {},
                progress: {
                  type: 'throbber'
                },
                event: 'click tap'
              });

              myAjax.options.success = function (response, status) {
                //Trigger Attach Behaviors
                setTimeout(function(){Drupal.attachBehaviors($(myAjax.selector))}, 0);
                // Sanity check for browser support (object expected).
                // When using iFrame uploads, responses must be returned as a string.
                if (typeof response == 'string') {
                  response = $.parseJSON(response);
                }

                return myAjax.success(response, status);
              }

              $link.addClass("middone"); //add class when we're done

            }
          }
        }
      }
    })(jQuery);

La función de éxito es copiar y pegar el valor predeterminado de ajax.js con una línea agregada para volver a asociar los comportamientos. Por alguna razón, Drupal.attachBehaviorstiene que estar dentro de un temporizador. No puedo tenerlo solo por una razón que ignoro.

Dejaré esta pregunta abierta para algunos en caso de que alguien pueda encontrar una solución más elegante y / o explicar la rareza del temporizador.

Muchas gracias

JSL
fuente
¿Por qué querrías unir comportamientos una y otra vez? se vuelven a ejecutar automáticamente en cualquier elemento nuevo creado con AJAX, entonces, ¿por qué no es suficiente?
Mołot
los comportamientos no se vuelven a asociar automáticamente en mi caso. thx
JSL

Respuestas:

1

Puede ser complicado adjuntar comportamientos ajax al contenido que se devuelve desde una solicitud ajax. Sin embargo, es posible.

Aunque su fragmento de código hook_menu parece incompleto, suponiendo que sea correcto (devuelve $ items y la función está cerrada): en su caso, es posible que solo necesite ajustar la devolución de llamada de entrega a 'ajax_deliver'

es decir:

/**
 * Implements hook_menu
 */
function mymodule_menu() {

  $items['mypath/%/%/ajax'] = array(
    'title' => 'My title',
    'page callback' => 'mymodule_ajax_callback',
    'page arguments' => array(1,2),
    'access arguments' => array('access content'),
    'delivery callback' => 'ajax_deliver',
    'theme callback' => 'ajax_base_page_theme',
    'type' => MENU_CALLBACK,
  );

  return $items;

}
David Thomas
fuente
Gracias por la sugerencia, pero esto tampoco funciona. Uso su código en hook_menu y los comportamientos no se vuelven a unir automáticamente.
JSL
1

Después de algunas depuraciones, me di cuenta de que el problema no estaba en mi código.

El problema radicaba en otro módulo, en mi caso el módulo colorbox, que fue la fuente de un error js en su propia función de comportamiento. Creo que el error provocó la interrupción del proceso de comportamiento de conexión y, como tal, mi propia función de comportamiento no se volvió a conectar. El error se pudo ver en la consola.

El error de colorbox: en 7.24

Uncaught TypeError: Cannot read property 'transition' of undefined colorbox_inline.js?

y en 7.25

Uncaught TypeError: Cannot read property 'mobiledetect' of undefined

Mi solución fue deshabilitar el módulo colorbox.

Muchas gracias a todos los que ayudaron.

JSL
fuente
1

No puedo comentar la primera respuesta , pero puede establecer la propiedad para colorbox en la configuración. Por ejemplo:

myAjax = new Drupal.ajax('#' + $uilink[i].id, $link, {
  url: $link.attr('data-url'),
  effect: 'fade',
  settings: {
    colorbox: {
      mobiledetect: false
    }
  },
  progress: {
    type: 'throbber'
  },
  event: 'click tap'
});
Ilya Krigouzov
fuente