¿Cómo enviar un formulario web mediante programación con Ajax?

8

Estoy trabajando en una implementación de Ajax para el envío del formulario web en Drupal 7. No pude encontrar nada bueno hookpara alterar el botón de envío del formulario web y agregar '#ajax' en el formulario, así que eché un vistazo a un módulo Drupal 6 que implementa esta funcionalidad desde un script externo.

Así que decidí ir con mi propio módulo y código JavaScript para enviar una solicitud de publicación de Ajax a una devolución de llamada de menú personalizada que he definido hook_menu()en Drupal 7.

La parte de JavaScript funciona bien, pero tengo problemas para enviar el formulario web mediante programación.

Aquí está mi código JavaScript:

function formSubmit(event, formId) {

  event.preventDefault();

  var form = jQuery("#" + formId);
  var postData = form.serialize();
  var nodeId = formId.substring(20);
  var msg = '';

  msg += form.find('#edit-submitted-name').attr('value') ? '' : 'Please enter your name';
  console.log(form.find('#edit-submitted-name').attr('value'));
  console.log(form.find('#edit-submitted-e-mail').attr('value'));

  if(msg) {
    alert(msg);
  } else {
    jQuery.ajax({
      url: Drupal.settings.basePath + 'webform_ajax/' + nodeId,
      fid:formId,
      type: 'POST',
      data: postData,
      success: function(ajaxData) {
        console.log(ajaxData);
        console.log('Hello world');
        // can't get here
      }
    });
  }
}

Y mi código de módulo (basado en el módulo webform_ajax):

function custom_menu() {
  $items = array();
  $items['webform_ajax/%'] = array(
    'page callback' => '_custom_webform_ajax',
    'page arguments' => array(1,2),
    'access callback' => '_custom_webform_ajax_access',
  );
  return $items;
}

function _custom_webform_ajax($nid, $data) {
  //$sid = $_POST['details']['sid'];

  $local_POST = $_POST;
  $form_build_id = $_POST['form_build_id'];

  $form_id = 'webform_client_form_' . $nid;

  $node = node_load($nid);

  $submission = array();
  $form_state = array();

  $form = form_get_cache($form_build_id, $form_state);
  $form_array = drupal_rebuild_form($form_id, $form_state, array($form_state, $node, $submission), $form_build_id);
  $form_state['clicked_button'] = $form_array['actions']['submit'];

  if (is_array($local_POST['submitted'])) {
    foreach ($local_POST['submitted'] as $submit_index => $submit) {
      $form_state['storage']['submitted'][$submit_index] = $submit;
      $form_state['values']['submitted'][$submit_index] = $submit;
    }
  }

  // Clearing empty values from $form_state
  if (is_array($form_state['values']['submitted'])) {
    foreach ($form_state['values']['submitted'] as $value_index => $value) {
      if (!$value) {
        unset($form_state['values']['submitted'][$value_index]);
      }
    }
  }

  // Executing the pressed button action
  drupal_execute($form_id, $form_state, $node, array());

  // Get the HTML for the error messages
  $error_html = theme('status_messages', 'error');

  // Building the resulting form after the processing of the button
  $form_array = drupal_rebuild_form($form_id, $form_state, array($form_state, $node, $submission), $form_build_id);
  $form = drupal_render_form($form_id, $form_array);

  return drupal_json_output(array(
    'message' => $error_html,
    'status' => 'sent',
  ));

}

function _custom_webform_ajax_access() {
  // Todo: Add webform access conditions
  return true;
}

Cuando envío mi formulario obtengo 500 errores de servidor.

Supongo que las API de formularios D6 y D7 son bastante diferentes y no estoy seguro de dónde comenzar a tener este código funcionando. He intentado depurarlo, pero no puedo entender qué genera los 500 errores.

Uso webform 3 y el módulo que tomé el código también se basa en la versión 3 de webform pero para Drupal 6. Pero ambos módulos deben proporcionar las mismas funciones y el mismo tipo de funcionalidades. Primera solución: puede provenir de los valores que paso que no serían compatibles con la api del formulario D7.

En mi registro tengo:

Argument 1 passed to drupal_array_nested_key_exists() must be an array, null given, called in D:\wamp\www\productionsite\includes\form.inc on line 1986 and defined in drupal_array_nested_key_exists() (line 6296 of D:\wamp\www\productionsite\includes\common.inc).

- EDITAR -

Estoy depurando línea por línea ahora, al final este código podría valer la pena convertirse en un módulo D7;)

En la documentación de D7 encontré que los argumentos drupal_rebuild_form () han cambiado de D6, y que $form_stateya no pueden estar vacíos en esta etapa, así que actualicé mi código de esta manera:

$form_state = array('submitted' => false, 'values' => array());
$form = form_get_cache($form_build_id, $form_state);
$form_array = drupal_rebuild_form($form_id, $form_state, $form);

Ahora estoy tratando de encontrar el equivalente de drupal_execute (), que ya no existe en D7.

- Editar (2) -

Lo hice funcionar hace unos días y vuelvo para compartir la solución, y tal vez obtener algunos consejos y sugerencias de mejoras.

<?php

function custom_menu() {
  $items = array();
  $items['webform_ajax/%'] = array(
    'page callback' => '_custom_webform_ajax',
    'page arguments' => array(1,2),
    'access callback' => '_custom_webform_ajax_access',
  );
  return $items;
}

function _custom_webform_ajax($nid, $data) {

  $local_POST = $_POST;
  $form_build_id = $_POST['form_build_id'];

  $form_id = 'webform_client_form_' . $nid;

  $node = node_load($nid);

  $submission = array();
  $form_state = array(
    'submitted' => false, 
    'values' => array(),
    'build_info' => array(
      'args' => array(
        $node,
        array(),
        FALSE
      )
    )
  );

  $form = form_get_cache($form_build_id, $form_state);
  $form_array = drupal_rebuild_form($form_id, $form_state);

  // Add the clicked button before processing the form
  $form_state['clicked_button'] = $form_array['actions']['submit'];

  if (is_array($local_POST['submitted'])) {
    foreach ($local_POST['submitted'] as $submit_index => $submit) {
      $form_state['values']['submitted'][$submit_index] = $submit;
    }
  }

  // Clearing empty values from $form_state
  if (is_array($form_state['values']['submitted'])) {
    foreach ($form_state['values']['submitted'] as $value_index => $value) {
      if (!$value) {
        unset($form_state['values']['submitted'][$value_index]);
      }
    }
  }

  $form_state['values']['details']['nid'] = $nid;

  // Executing the pressed button action
  drupal_build_form($form_id, $form_state);

  return drupal_json_output(array(
    'message' => t('Your submission has been received. Thank you for contacting us.'),
    'status' => 'sent',
  ));  

}

function _custom_webform_ajax_access() {
  // TODO: Add user role / perm check
  return true;
}

Para ir un paso más allá, me gustaría obtener ahora los errores del formulario procesado para poder enviarlos de regreso con el objeto json. Algunas ideas ?

E. de Saint Chamas
fuente

Respuestas:

4

Estaba haciendo algo similar y encontré la solución de E. de Saint Chamas para trabajar principalmente para mí. Sin embargo, había algunas cosas que necesitaba agregar:

Primero, tuve que agregar esto a la matriz form_state antes de procesar el formulario

'method' => 'post',

Luego, hacia la parte inferior, algunos ajustes para procesar el formulario y devolver mensajes de error si los hay:

  // Prevent the form from redirecting the request
  $form_state['no_redirect'] = TRUE;
  // Executing the pressed button action
  drupal_build_form($form_id, $form_state);
  // See if the form submitted successfully
  if (!$form_state['executed']) {
    // If the form didn't submit successfully, get the errors
    // which are set bu drupal_set_message
    $messages = drupal_get_messages('error');
    $messages = implode('<br />', $messages['error']);
  }
  else {
    // If form submitted successfully, create a nice message.
    $messages = "Thanks for contacting us! We will let you know when the Beta is live!";
  }
  // drupal_json_output seems to confuse some browsers, who want to save as a file 
  print drupal_json_encode(array(
    'message' => $messages,
    'status' => $form_state['executed'],
  ));

No estoy seguro de si esta es la mejor manera de hacerlo, pero descubrí que funcionó para mí. Por supuesto, es posible que desee seguir adelante y procesar los mensajes de error y devolver un cuadro de mensaje de error completamente procesado, y además, puede extraer el "mensaje de confirmación" de la matriz $ form_state para que pueda controlar el mensaje de éxito desde el interfaz de usuario webform.

Wesnick
fuente
Esto es genial, pero sigo obteniendo un error ($ form_state ['execute'] = False). Y no hay nada en drupal_get_messages ('error'). Me pregunto cómo puedo depurar esto.
cybertoast
Debo aclarar que intento enviar por curl, como curl -vvv -X POST -H "X-Requested-With: XMLHttpRequest" -d 'submit [contact_fullname] = my% 20name & submit [contact_email] = test% 40example. com & submit [contact_message] = test% 20message '" localhost / fubar / 31 ". El contenido se envía y se completa form_state, pero drupal_form_build () no se ejecuta / envía.
cybertoast
-1

Dígame si estoy equivocado, pero dado que el envío de un formulario web es un nodo, ¿por qué no crear directamente el nodo mediante programación page callback(con validación de campo (o podría hacerlo antes de enviar usando JavaScript))

Podría ser algo como

if(!function_exists("node_object_prepare"))
{
  include_once(drupal_get_path('module', 'node') . '/node.pages.inc');
}
$node = new stdClass();                                                         
$node->is_new = TRUE;
$node->type = 'YOUR_NODE_TYPE_HERE';                                
node_object_prepare($node);

// then all the fields you need

node_validate($node);
$node = node_submit($node);
node_save($node);
$nid = $node->nid;

Et voilà! :)

Gueno
fuente
3
En realidad, los envíos de formularios web no son nodos. Webform almacena los envíos en sus propias tablas. Por lo tanto, no podemos tener un nuevo nodo creado para agregar el envío. Por otra parte me gustaría tener todo el flujo de trabajo de validación de formulario web que se dispare una vez ejecutada la forma por lo que comprueba los campos requeridos, etc ...
E. de Saint Chamas