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

 
Great Websites are Created before the First Line of Code is Written

Các trang web tuyệt vời được tạo trước khi dòng code đầu tiên được viết

When you’re surrounded by a team of awesome developers, you might think that a statement such as, “Great Websites are Created before the First Line of Code is Written,

Web 2.0 - là một cuộc cách mạng trên thế giới

Web 2.0 - là một cuộc cách mạng trên thế giới

Là nền tảng để các dịch vụ mới của Social Media phát triển, web 2.o được xem là một cuộc cách mạng trên thế giới mạng, thế hệ web mới có những thay đổi quan trọng không chỉ ở nền tảng công nghệ mà còn cả ở cách thức sử dụng – hình thành nên môi trường cộng đồng, ở đó mọi người cùng tham gia đóng góp cho xã hội “ảo” chứ không chỉ “duyệt và xem”.

Các thành phần theming views field templates in Drupal 7

Các thành phần theming views field templates in Drupal 7

Today I needed to work the Nivo featured content slider into a D7 site I’m building; no problem you might say, there’s a module for that! Sadly that’s not the case as it’s far from ready (there is now a working D7 plugin for Nivo slider please see the links for details).

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

 

Diet con trung