Come inviare il modulo web a livello di codice con Ajax?


8

Sto lavorando su un'implementazione Ajax per l'invio di Webform su Drupal 7. Non sono riuscito a trovare alcunché utile hookper modificare il pulsante di invio Webform e aggiungere '#ajax' nel modulo, quindi ho dato un'occhiata a un modulo Drupal 6 che implementa questa funzionalità da uno script esterno.

Così ho deciso di utilizzare il mio modulo e il mio codice JavaScript per inviare una richiesta di post Ajax a un callback di menu personalizzato che ho definito hook_menu(), in Drupal 7.

La parte JavaScript funziona bene ma sto riscontrando problemi nel tentativo di inviare il modulo Web a livello di codice.

Ecco il mio codice 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
      }
    });
  }
}

E il mio codice del modulo (basato sul modulo 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;
}

Quando invio il modulo ricevo 500 errori del server.

Immagino che le API dei moduli D6 e D7 siano piuttosto diverse e non sono sicuro da dove iniziare a far funzionare questo pezzo di codice. Ho provato a eseguire il debug ma non riesco a capire cosa sta generando i 500 errori.

Uso webform 3 e il modulo che ho preso il codice si basa anche sulla versione 3 di webform ma per Drupal 6. Ma entrambi i moduli dovrebbero fornire le stesse funzioni e lo stesso tipo di funzionalità. Prima soluzione alternativa: potrebbe derivare dai valori che passo che non sarebbero compatibili con D7 da API.

Nel mio registro ho:

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).

-- MODIFICARE --

Sto eseguendo il debug riga per riga ora, alla fine questo pezzo di codice potrebbe valere la pena di diventare un modulo D7;)

Ho trovato nella documentazione D7 che gli argomenti drupal_rebuild_form () sono cambiati da D6 e che $form_statein questa fase non possono più essere vuoti, quindi ho aggiornato il mio codice in questo modo:

$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);

Ora sto cercando di trovare l'equivalente di drupal_execute (), che non esiste più in D7.

- Modifica (2) -

Ho funzionato qualche giorno fa e sono tornato per condividere la soluzione e forse ottenere alcuni consigli e suggerimenti di miglioramento.

<?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;
}

Per fare un ulteriore passo, vorrei ora ottenere gli errori dal modulo elaborato in modo da poterli rispedire con l'oggetto json. Qualche idea ?

Risposte:


4

Stavo facendo qualcosa di simile e ho trovato la soluzione di E. de Saint Chamas per lavorare principalmente per me. Tuttavia, c'erano alcune cose che dovevo aggiungere:

Innanzitutto, ho dovuto aggiungere questo all'array form_state prima di elaborare il modulo

'method' => 'post',

Quindi, verso il basso, alcune modifiche per elaborare il modulo e restituire eventuali messaggi di errore:

  // 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'],
  ));

Non sono sicuro che sia il modo migliore per farlo, ma ho scoperto che ha funzionato per me. Naturalmente, potresti voler semplicemente andare avanti e visualizzare i messaggi di errore e restituire una finestra di messaggio di errore con rendering completo e, inoltre, potresti estrarre il "messaggio di conferma" dall'array $ form_state in modo da poter controllare il messaggio di successo dal interfaccia utente webform.


Questo è fantastico, ma continuo a ricevere un errore ($ form_state ['eseguito'] = Falso). E non c'è nulla in drupal_get_messages ('errore'). Mi chiedo come posso eseguire il debug di questo.
cybertoast,

Dovrei chiarire che sto provando a inviare tramite curl, come 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 ". Il contenuto viene inviato e form_state viene popolato, ma drupal_form_build () non viene eseguito / inviato.
cybertoast,

-1

Dimmi se sbaglio, ma poiché l'invio di un modulo web è un nodo, perché non creare direttamente il nodo programmaticamente nel tuo page callback(con la convalida del campo (o potresti farlo prima di inviare usando javascript))

Potrebbe essere qualcosa del genere

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à ! :)


3
In realtà gli invii di moduli web non sono nodi. Webform memorizza gli invii nelle proprie tabelle. Quindi non è possibile creare un nuovo nodo per aggiungere l'invio. Inoltre vorrei che l'intero flusso di lavoro di convalida del modulo web venisse attivato una volta eseguito il modulo in modo che controlli i campi richiesti, ecc ...
E. de Saint Chamas
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.