Passing stickyGet values from within a wizard form onSubmit callback

I’ve been playing around with the Wizard component to make a wizard that allows multiple objects to be created at once. For example, I have a “scenario” class, which is a foundation for other items to be created within, and a number of other classes, such as “events” that are created within a given scenario. I want my wizard to help the user create a scenario, and then an event within that scenario.

My application uses stickyGet to tell each page what the currently active scenario is, and that’s where the problem arises. My wizard gets created with all the steps to create a scenario, and then an event within that scenario. However, once I finish the step where the scenario is created I’m having difficulty finding a good way to set the stickyGet for the rest of the wizard to use.

If there’s no form needed on the final step of the scenario creation, I can use something like

$w->buttonNext->link([$w->urlNext(), ‘scenario_id’ => $scenario[‘id’]]);

just fine. However, if I do have a form on that final step, I need a javascript equivalent of it that I can return from my onSubmit function. I’ve been able to do something like:

return (new \atk4\ui\jsExpression(‘window.location.href = []’, [$w->urlNext(), ‘scenario_id’ => $f->model[‘id’]]));

That does work, but it just seems awkward to me – like there should be a better way. Is there? Or should I just learn to love the above?

Thanks in advance for your help.

I use a similar jsExpression in my wizard->addFinish step. It seems to work and I don’t know of a better solution :slight_smile:

This deserves a tutorial on it’s own, but I’ll explain a best way to do that.

You don’t want create things prematurely
By design, wizards shouldn’t create things unless you press “Finish” button. So on the steps, you can ask user to fill out forms etc, but don’t save them into database just yet.

Wizard implements SessionTrait (https://github.com/atk4/ui/blob/develop/src/Wizard.php#L12), therefore you can easily use $wizard->memorize and $wizard->recall (https://agile-core.readthedocs.io/en/latest/session.html)

Here is example code:

$data = $wizard->recall('event', []);
$scenario = new Scenario(new Persistence\Array_($data));
$this->app->addHook('beforeExit', function() use($wizard, $data) {
     // store data
     $wizard->memorize('event', $data); 
});

$wizard->add('Form')->setModel($scenario->tryLoadAny());

You can also use CRUD here:

$wizard->add('CRUD')->setModel($scenario->refModel('Events'));

Finally on the finish step you can display Card + Table of items to be added and if user confirms, proceed storing them using either https://agile-data.readthedocs.io/en/develop/persistence.html#store-a-specific-record or DeepCopy (https://github.com/atk4/data/pull/359)

If you want more info, join our weekly call, Wednesday 6pm London time or here: https://gitter.im/atk4/atk4

I’d like to question this foundational assumption. I accept that Wizard isn’t designed to create things before the Finish button is pressed, but I question why this should be so. I have a related topic that I posted where I suggested a modification to the Wizard class because it wasn’t letting me create Wizards with more than 10 or 11 steps, and that’s related to this issue.

To draw an analogy, my application is using a Wizard to create a fully-furnished house. The first section of the Wizard uses a number of steps to create the house structure itself. Once that’s done, the Wizard has a step to allow the user to say that he’d like to (in this analogy) do one or more of design the bathroom, furnish the bedroom, etc. Those steps can only be taken in the context of a specific house, so at that point I’ve saved the house and want to use stickyGet to pass along which house we’re taking about. While I agree you wouldn’t want to save a partially-designed house, in this analogy, or a partially-designed bathroom, there’s a lot of potential value in saving the house structure once that part’s done, then proceeding to the next step of the wizard to design the bathroom. If the user interrupts that phase, they can always go back and redesign the bathroom from scratch, but there’s no need for them to have to redesign the house structure.

Well if in your case you must create items right away, that’s also possible. Simply $wizard->memorize(‘id’, $scenario->id); - even simpler.

Another thing worth noting is that you can register sticky with any view, not only API, so try $wizard->stickyGet(’…’);, it should make it sticky only in the context of the wizard. If that’s not working still - perhaps it’s due to the order how links are generated. Perhaps things can be moved or perhaps it’s not calling $this->url() ($this->app->url is the obsolete way).