Hướng dẫn tạo multi-step forms có sử dụng ctools multistep wizard

Hướng dẫn tạo multi-step forms có sử dụng ctools multistep wizard

In the previous post I gave an example of Ctools modal API as being operated with a single form. In this one you'll be given an insight into my experiences with another powerful tool, namely Ctools multistep wizard as being used with or without the modal API involvement.

let's cut the lyrics about all those pretty-looking node, user, registration forms that are done with or without the help of this tool. The popups being used, this process will move to a higher visual level.

So, as usual, we start with hook_menu (). We declare the page which we will be calling a multi-step form and the page of the form itself:

/**
 * Implements hook_menu().
 */
function multi_example_menu() {
  $items['example/%ctools_js/form'] = array(
    'title' => 'Example form',
    'page callback' => 'multi_example_form',
    'page arguments' => array(1),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['example-link'] = array(
    'title' => 'Example form link',
    'page callback' => 'multi_example_link',
    'page arguments' => array(1),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  return $items;
}

Functions that generate category:

/**
 * Page callback: Handles multistep precessing.
 *
 * @return string
 *   Multistep wizard output.
 *
 * @see multi_example_menu()
 */
function multi_example_form($js = NULL, $step = NULL) {
  if ($js) {
    ctools_include('modal');
    ctools_include('ajax');
  }
 
  // Define array for ctools multistep wizard.
  $form_info = array(
    'id' => 'multi_example',
    'path' => "example/" . ($js ? 'ajax' : 'nojs') . "/form/%step",
    'show trail' => TRUE,
    'show back' => TRUE,
    'show cancel' => TRUE,
    'show return' => FALSE,
    'next callback' =>  'multi_example_wizard_next',
    'finish callback' => 'multi_example_wizard_finish',
    'cancel callback' => 'multi_example_wizard_cancel',
 
   // Define forms order.
    'order' => array(
      'start' => t('Enter your info'),
      'second' => t('What do you know?'),
      'third' => t('What do think about drupal?'),
      'fourth' => t('Do you like cookies?'),
    ),
 
   // Define forms
    'forms' => array(
      'start' => array(
        'form id' => 'multi_example_form_start'
      ),
      'second' => array(
        'form id' => 'multi_example_form_second'
      ),
      'third' => array(
        'form id' => 'multi_example_form_third'
      ),
      'fourth' => array(
        'form id' => 'multi_example_form_fourth'
      ),
    ),
  );
 
  // We're not using any real storage here, so we're going to set our
  // object_id to 1. When using wizard forms, id management turns
  // out to be one of the hardest parts. Editing an object with an id
  // is easy, but new objects don't usually have ids until somewhere
  // in creation.
  //
  // We skip all this here by just using an id of 1.
  $object_id = 1;
 
  if (empty($step)) {
 
    // We reset the form when $step is NULL because that means they have
    // for whatever reason started over.
    multi_example_cache_clear($object_id);
    $step = 'start';
  }
 
  // This automatically gets defaults if there wasn't anything saved.
  $object = multi_example_cache_get($object_id);
 
  // live $form_state changes.
  $form_state = array(
    'ajax' => $js,
 
    // Put our object and ID into the form state cache so we can easily find it.
    'object_id' => $object_id,
    'object' => &$object,
  );
 
  // Send this all off to our form. This is like drupal_get_form only wizardy.
  ctools_include('wizard');
  $form = ctools_wizard_multistep_form($form_info, $step, $form_state);
  $output = drupal_render($form);
  if ($js) {
 
    // If javascript is active, we have to use a render array.
    $commands = array();
    if ($output === FALSE || !empty($form_state['complete'])) {
      // Dismiss the modal.
      $commands[] = ajax_command_html('#ctools-sample', render(multi_example_get_result($object)));
      $commands[] = ctools_modal_command_dismiss();
    }
    elseif (!empty($form_state['cancel'])) {
 
      // If cancelling, return to the activity.
      $commands[] = ctools_modal_command_dismiss();
    }
    else {
      $commands = ctools_modal_form_render($form_state, $output);
    }
    print ajax_render($commands);
  }
  else {
    if ($output === FALSE || !empty($form_state['complete'])) {
 
      return render(multi_example_get_result($object)) . "\n\r" . l(t('Back'), 'example-link');
    }
    elseif (!empty($form_state['cancel'])) {
      drupal_goto('example-link');
    }
    else {
      return $output;
    }
  }
}

We need some place to keep the interim results in. For this the cache is beig used by Ctools:

/**
 * Clears the wizard cache.
 *
 * @param integer $id
 *   cache id.
 */
function multi_example_cache_clear($id) {
  ctools_include('object-cache');
  ctools_object_cache_clear('multi_example', $id);
}
 
/**
 * Stores our little cache so that we can retain data from form to form.
 *
 * @param integer $id
 *   cache id.
 * @param object $object
 *   object with form values.
 */
function multi_example_cache_set($id, $object) {
  ctools_include('object-cache');
  ctools_object_cache_set('multi_example', $id, $object);
}
 
/**
 * Gets the current object from the cache, or default.
 *
 * @param integer $id
 *   cache id.
 *
 * @return object
 *   cache with stored stuff.
 */
function multi_example_cache_get($id) {
  ctools_include('object-cache');
  $object = ctools_object_cache_get('multi_example', $id);
  if (!$object) {
    // Create a default object.
    $object = new stdClass;
  }
 
  return $object;
}

We also need separate functions reserved for “Continue”, “Cancel”, “Finish” buttons:

/**
 * Handles the 'next' click on the add/edit pane form wizard.
 *
 * All we need to do is store the updated pane in the cache.
 */
function multi_example_wizard_next(&$form_state) {
  multi_example_cache_set($form_state['object_id'], $form_state['object']);
}
 
/**
 * Handles the 'finish' click on teh add/edit pane form wizard.
 *
 * All we need to do is set a flag so the return can handle adding
 * the pane.
 */
function multi_example_wizard_finish(&$form_state) {
  $form_state['complete'] = TRUE;
}
 
/**
 * Handles the 'cancel' click on the add/edit pane form wizard.
 */
function multi_example_wizard_cancel(&$form_state) {
  $form_state['cancel'] = TRUE;
}

Here are the forms proper, with the processed values herein:

/**
 * Generates first form.
 *
 * @ingroup forms
 */
function multi_example_form_start($form, &$form_state) {
  $form['name'] = array(
    '#type'          => 'textfield',
    '#title'         => t('First name'),
    '#default_value' => isset($form_state['object']->name) ? $form_state['object']->name : '',
    '#required'      => TRUE,
  );
  $form['surname'] = array(
    '#type' => 'textfield',
    '#title' => t('Last name'),
    '#default_value' => isset($form_state['object']->surname) ? $form_state['object']->surname : '',
    '#required' => TRUE,
  );
  return $form;
}
 
/**
 * Handles submit of first form.
 */
function multi_example_form_start_submit($form, &$form_state) {
  $form_state['object']->name = $form_state['values']['name'];
  $form_state['object']->surname = $form_state['values']['surname'];
}
 
/**
 * Generates second form.
 *
 * @ingroup forms
 */
function multi_example_form_second($form, &$form_state) {
  $form['know'] = array(
    '#type'          => 'checkboxes',
    '#options'       => array('php' => t('PHP'), 'css' => t('CSS'), 'jquery' => t('Jquery'), 'unix' => t('Unix')),
    '#default_value' => isset($form_state['object']->know) ? $form_state['object']->know : array(),
  );
  return $form;
}
 
/**
 * Handles submit for second form.
 */
function multi_example_form_second_submit($form, &$form_state) {
  $form_state['object']->know = $form_state['values']['know'];
}
 
/**
 * Generates third form.
 *
 * @ingroup forms
 */
function multi_example_form_third($form, &$form_state) {
  $form['drupal'] = array(
    '#type'          => 'radios',
    '#options'       => array('awesome' => t('Awesome'), 'awful' => t('Awful')),
    '#default_value' => isset($form_state['object']->drupal) ? $form_state['object']->drupal : '',
    '#default_value' => 'awesome',
    '#required'      => TRUE,
  );
  return $form;
}
 
/**
 * Handles submit for third form.
 */
function multi_example_form_third_submit(&$form, &$form_state) {
  $form_state['object']->drupal = $form_state['values']['drupal'];
}
 
/**
 * Generates fourth form.
 *
 * @ingroup forms
 */
function multi_example_form_fourth($form, &$form_state) {
  $form['work'] = array(
    '#type'          => 'radios',
    '#options'       => array('yes' => t('Yes'), 'no' => t('No')),
    '#default_value' => 'yes',
    '#required'      => TRUE,
  );
  return $form;
}
 
/**
 * Handles submit for fourth form.
 */
function multi_example_form_fourth_submit(&$form, &$form_state) {
  $form_state['object']->work = $form_state['values']['work'];
}
 
/**
 * Returns form results.
 *
 * @param object $object
 *   object with form values.
 *  
 * @return array
 *   returns renderable array for multistep form result output.
 */
function multi_example_get_result($object) {
  $out = array();
  $out[] = t('Your name: @name', array('@name' => $object->name));
  $out[] = t('Your surname: @surname', array('@surname' => $object->surname));
  $known = array_filter($object->know);
  if (count($known)) {
    $out[] = t('Your know: !know', array('!know' => implode(', ', $known)));
  }
  else {
    $out[] = t("You don't know anything");
  }
  $out[] = t('You think drupal is !mind', array('!mind' => $object->drupal));
  if ($object->work == 'yes') {
    $out[] = t('You like cookies :)');
  }
  else {
    $out[] = t("You don't like cookies :(");
  }
  return array('#theme' => 'item_list', '#items' => $out, '#title' => t('Results:'));
}

That's about it. The relevant live example can be found here. The module can be downloaded at the link below.

I do appreciate your time.

Bạn thấy bài viết này như thế nào?: 
Average: 5 (1 vote)
Ảnh của Khanh Hoang

Khanh Hoang - Kenn

Kenn is a user experience designer and front end developer who enjoys creating beautiful and usable web and mobile experiences.

Tìm kiếm bất động sản

 

Advertisement

 

jobsora

Dich vu khu trung tphcm

Dich vu diet chuot tphcm

Dich vu diet con trung

Quảng Cáo Bài Viết

 
Thị trường bất động sản chưa có điểm sáng nào

Thị trường bất động tiếp tục thê thảm trong thời gian tới

Hàng loạt công ty tư vấn bất động sản vừa có báo cáo về tình hình thị trường bất động sản quý 2 năm 2012 và dự báo thị trường trong quý tiếp theo. Theo những báo cáo này thì thị trường bất động tiếp tục thê thảm trong thời gian tới và chưa có điểm sáng nào có thể phá băng thị trường, toàn bộ phân khúc đang bị tê liệt.

Showing Different Drupal Themes on Different Pages

Hiển thị Drupal Themes mới trên 1 Pages khác

This tutorial was written after a request in one of this week's Drupal classes: "How do I show different Drupal themes on different pages?". The answer is a module called "Sections":

Top máy tính bảng “tí hon” đáng mua nhất năm 2013

Top máy tính bảng “tí hon” đáng mua nhất năm 2013

Hiện nay, nếu không phải người hay quan tâm đến "đồ chơi" công nghệ, bạn có thể bị "choáng ngợp" trước thị trường máy tính bảng với muôn vàn chủng loại, kích thước và giá cả

Công ty diệt chuột T&C

 

Diet con trung