4

Brief

I'm using Entityform with Field group configured as Multipage with AJAX enabled for multipage groups provided by Field Group Ajaxified Multipage. So there are multiple steps and user can access (jump into) these steps through provided Multipage jumplist block.

See some sample images (to give you some visual idea):

field_group_ajaxify_multipage_overview.jpg multipage_jumplist.png

Problem

The problem is that form is AJAX (dynamic) and jumplist block is static (which is unaware of form context and links doesn't work) with some basic jQuery code (so it's not reloaded when user goes to next pages).

As result this is what happens:

  • when field_group multipage is AJAX, the form validation works per step, but jumplist doesn't work (jQuery events are not attached to the links and other steps are not available as it needs to be loaded via AJAX),
  • when field_group multipage is not AJAX, the jQuery events works, so you can switch between pages, but the server-side validation doesn't work as nothing is submitted on Next button

but I need both to work (server-side validation and jumplist links).

Further info

See also the following related issue.

I'm thinking to do:

  • jQuery, but I'm not sure how to access the step the user is in,
  • rendering navigation block (or jumplist it-self) with the AJAX form (dirty and tricky), e.g.

    // Implements hook_form_alter().
    $block = module_invoke('multipage_jumplist', 'block_view', 'd07d74b8ad268f8c'); 
    $rendered_block = theme('block', $block);
    $theme_code = theme('multipage_jumplist', $foo);
    $form['navigation'] = array('#markup' => $rendered_block);
    
  • not to use AJAX, but I need it for step-by-step server-side validation (FGAM+IFE),

  • create a session variable and pass to the JS (but the page won't be reloaded), or override jumplist theme hook, e.g.

    // hook_form_alter - Get step we're in.
    $step = empty($form_state['storage']['field_group_ajaxified_multipage_step']) ? 1 : $form_state['storage']['field_group_ajaxified_multipage_step'];
    $curr_step = &$_SESSION[end((arg())) . '-step'];
    $curr_step = max($step, $curr_step);
    // theme_multipage_jumplist_item - Parse the jumplist
    $visited_steps = &$_SESSION[end((arg())) . '-step'];
    if ($variables['counter'] <= $visited_steps) {
        // return either clickable item or not.
    }
    
  • customize/patch the module (where to start),

  • find some alternative solution

Question

How do I alter/update my multipage jumplist block (unaware of form context) based on the current step user is in having AJAXified form?

By updating I mean either knowing which element is active, or have ability to extend the behaviour and disable further steps (from being clickable) which weren't visited yet.

What's your suggestion? What would be the Drupal-way?

  • just a shot in the dark, can you try making that block as part of your form, just by hook_form_alter and then adding a markup field, this way it will automatically update on each step – arpitr Sep 16 '15 at 21:56
  • @arpitr Thanks, I was attempting to do so (I've pasted some sample code into post), but I forsaken this solution temporary, because of some initial scary warnings, secondly the disadvantage is that the navigation placement is within the content it-self. I'll try again if that would work, but meanwhile I'm checking if I can patch FGAM somehow to do AJAX calls more flexible, as the jumplist doesn't work at all when using AJAX for multipage field_group. – kenorb Sep 16 '15 at 22:30
1

This was implemented by patching/extending FGAM module and the following jQuery code:

/**
 * @file
 * Connect jumplist to their corresponding multipage panes.
 */
(function ($) {
  Drupal.behaviors.multipageJumplistFix = {
    step: 1,
    maxStep: 1,
    attach: function(context) {

      // Wrapper changes its id but it's id must be static!
      $('div[id^="foo-entityform-edit-form"][id$="form-ajax-wrapper"]').attr('id', 'foo-entityform-edit-form-form-ajax-wrapper');

      this.step = $('.multipage_step_change.active').attr('step');
      this.numSteps = $('.multipage-jumplist > li').length;
      this.maxStep = Math.max (this.maxStep, this.step);

      this.updateActive ();

    },
    updateActive: function () {
      $('.multipage-jumplist a').removeClass('active');
      $('.multipage-jumplist a[step=' + this.step + ']').addClass ('active');

      for (var i = 1; i <= this.numSteps; ++i) {
        if (i > this.maxStep + 1)
          $('.multipage-jumplist a[step=' + i + ']').addClass ('disabled');
        else
          $('.multipage-jumplist a[step=' + i + ']').removeClass ('disabled');
      }
    }
  };

  $(function () {
    $('.multipage-jumplist').html($('.multipage-jumplist').html());

    $('.multipage-jumplist a').each(function (index, el, e) {
      el.step = index + 1;
      $(this).click (function (e) {
        console.log (this.step);
        if (!$(this).hasClass('disabled')) {
          $('.multipage_step_change[step=' + this.step + ']').trigger('mousedown');
          return false;
        }
      });
    });

    Drupal.behaviors.multipageJumplistFix.attach();
  });
})(jQuery);

which is loaded from the theme and then few enhancements in template.php:

/**
 * Implements hook_form_alter().
 */
function theme_form_alter(&$form, &$form_state, $form_id) {
  if (strpos($form_id, 'entityform_edit_form') !== FALSE) {
    drupal_add_js(drupal_get_path('theme', 'foo') .'/js/eforms.js');

    $step = empty($form_state['storage']['field_group_ajaxified_multipage_step']) ? 1 : $form_state['storage']['field_group_ajaxified_multipage_step'];
    $curr_step = &$_SESSION[end((arg())) . '-step'];
    $curr_step = max($step, $curr_step);

    unset($form['actions']['prev']);
    unset($form['actions']['next']);

    if (!empty($form['#multipage_jumplist_instances'])) {
      $num_steps = count($form['#multipage_jumplist_instances'][0]['panes']);

      for ($i = 1; $i <= $num_steps; ++$i) {
        $active = $step == $i;
        $is_prev = $i == ($step - 1);
        $is_next = $i == ($step + 1);
        $form['actions'][$is_prev ? 'prev' : ($is_next ? 'next' : ('step' . $i))] = array(
          '#type' => 'submit',
          '#value' => $is_next ? t('Continue') : ($is_prev ? t('Previous') : ('Step' . $i)),
          '#id' => 'step' . $i,
          '#attributes' => array('step' => $i, 'style' => (($is_prev || $is_next) ? '' : 'display: none'), 'class' => array('multipage_step_change', ($active ? 'active' : ''))),
          '#submit' => array('theme_multipage_step_change_submit'),
          '#ajax' => array(
            'wrapper' => 'foo-entityform-edit-form-form-ajax-wrapper',
            'callback' => '_field_group_ajaxified_multipage_form_callback',
          ),
        );
        if ($i < $step) {
          // We don't want validation when going back.
          $form['actions']['step' . $i]['#limit_validation_errors'] = array();
        }
      }
    }

  }
}

function theme_multipage_step_change_submit(&$form, &$form_state) {
  $step = substr($form_state['triggering_element']['#id'], 4);

  $form_state['triggering_element']['#array_parents'] = array('actions', 'step' . $step);
  $form_state['triggering_element']['#parents']       = array('actions', 'step' . $step);

  _field_group_ajaxified_multipage_form_submit($form, $form_state);

  $form_state['rebuild'] = TRUE;
}

This can be also extended by theme overrides for: theme_multipage_jumplist() and theme_multipage_jumplist_item().

Hope that helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.