Is it possible to skip to a different step based off a user's response? Here is my use case, I have a multistep form that requires a set of specific questions to be answered based off the number provided by the user. This is a for a medical study and we ask "how many partners have you had?". We cap this at five partners and there are maximum of 16 questions over five steps that the user has to fill out. I decided to build out all possible fields, that's 5 partners with 5 sections each. What I would like is on the final step for each partner we check if the user has more partners to fill out. If so, send them them to the next partner step and if not send them to another step.

Attached is a flow chart of this process.

Comments

robpowell created an issue. See original summary.

jrockowitz’s picture

Right now, this is not possible. Adding conditional wizard page logic is not in the immediate roadmap for the YAML form module's first release. You can hide/show elements using the #states API.

robpowell’s picture

Thank you for the response, if you have the time - could you talk me through how you would approach such a solution?

My thought process is the following:

  1. Use form alter hook to identify number of partners
  2. Add logic to save / submit that checks if the user needs to see next partner questions
  3. based off of two, route them to the right step

One part I haven't been able to track down, is how are you handling the routing for multistep? Can you point me to where this is in the code or the docs?

Also, if you think this approach isn't worth it, that would be great to know.

Thanks!

jrockowitz’s picture

The YAML Form module is still in beta and I am still trying to improve (and fix) the workflow and altering hooks.

The routing for multistep is handled in \Drupal\yamlform\YamlFormSubmissionForm. I don't think you can alter it.

For time being you might consider not using a multistep form and using the #states API in one long form.

robpowell’s picture

Great I'll look into it. Yea, after my last post, I decided the best route was to go with states AP so I have already moved it over.

Thanks again for the prompt replies. I really love this module and plan on contributing when I get some bandwidth.

robpowell’s picture

So here is another way I handled this, but it works better with the following use case. Say you have a ten step form and after step four a user doesn't qualify to continue. You want to track those that don't qualify which requires saving the submission to the DB; this is the approach I used.

Once I realized that the submit button (which saves to DB) was available via form alter, I toggled the access value for both the submit and next buttons. The user(s) that don't qualify to continue will only see the submit button, while the user(s) that do, will see the next button.

$form['actions']['submit']['#access'] = TRUE;
$form['actions']['next']['#access'] = FALSE;

Finally, if you don't need to save the submission when they 'disqualify', you can always redirect the response to a node:

$does_not_qualify = new RedirectResponse('/node/1'); 
$does_not_qualify->send();
jrockowitz’s picture

I think to long term solution will be to setup a way to support setting #access to FALSE for wizard page and then remove the page from the wizard's paging logic.

The challenge is going to be determining where and how a developer should be able alter a wizard's page #access properties. Right now this is not possible using just hook_form_alter() because the wizard's pages have been completely initialized when hook_form_alter() is called.

robpowell’s picture

Right now this is not possible using just hook_form_alter() because the wizard's pages have been completely initialized when hook_form_alter() is called.

Well that might not be necessary because we can update wizard['current_page'] via the form_alter hook. I guess this can be described as spoofing what page the form_state is coming from but for now, it accomplishes what I need.

//find step
$wizard = $form_state->get('wizard');
$current_step = $wizard['current'];

//steps that can 'jump' to step_4_key
$step3_p1_q18 = array_search('step3_p1_q18', $wizard['pages']);
$step3_p2_q18 = array_search('step3_p2_q18', $wizard['pages']);
$step3_p3_q18 = array_search('step3_p3_q18', $wizard['pages']);
$step3_p4_q18 = array_search('step3_p4_q18', $wizard['pages']);

//skip destination
$step_4_key = array_search('step_4_std', $wizard['pages']);

//skip criteria
if (($current_step == $step3_p1_q18 && $partner_count == 1) ||
  ($current_step == $step3_p2_q18 && $partner_count == 2) ||
  ($current_step == $step3_p3_q18 && $partner_count == 3) ||
  ($current_step == $step3_p4_q18 && $partner_count == 4))  {
  // in the backend, this will be incremented by one
  $skip_to = $step_4_key - 1;
  $wizard['current'] = $skip_to;
  $form_state->set('wizard', $wizard);
}
fenstrat’s picture

Project: YAML Form » Webform
Version: 8.x-1.0-beta14 » 8.x-5.x-dev
jrockowitz’s picture

Status: Active » Fixed
Related issues: +#2826910: Extend wizard

@robpowell I refactored the multistep form wizard code and you will most likely need to update your code. The good news is that it should be much easier for you to hide/show and jump to pages within the multistep form wizard.

In a nutshell, you can now alter the multistep form wizard's 'pages' or 'current_page' which is stored in the form's state.

@see #2826910: Extend wizard

robpowell’s picture

@jrockowitz great, can you provide a quick example of how I should do it now?

jrockowitz’s picture

@robpowell please look at #2826910: Extend wizard.

You now just need to alter the form state's current page or pages using the page's machine name.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

robpowell’s picture

Just wanted to follow up with this. I was able to achieve this with the following code.

function goto_step($goto_step, $pages, \Drupal\Core\Form\FormStateInterface $form_state){
  //have a key as a string
  $all_keys = array_keys($pages);
  $goto_step_key = array_search($goto_step, $all_keys);
  if($goto_step_key > 0){
    //the backend pointer for page will add 1 so to go our page we must -1
    $form_state->set('current_page', $all_keys[$goto_step_key-1]);
  }
  else{
    //something went wrong
  }
}
jrockowitz’s picture

@robpowell Can you please post a recipe for other developers? Thanks

robpowell’s picture

@jrockowitz awesome. Done deal take a look #2850813: How to programmatically skip pages in wizard forms.