BasicDatapage asks for the name of the customer and the type of customer (Person or Company). By now you can probably
CustomerModelguess how the
CustomerModelobjects look, so we won't repeat them here.
View, it will respond to the
openModalcall. Let's imagine a button that opens the Wizard:
Nextbuttons are available whenever there are more pages either previous or next in the wizard.
Nextnavigation however, whether the wizard actually navigates to the next page is dependent upon the
completedstate of the current page. Every
completedproperty and a corresponding
isCompletedvariable you can manipulate.
Finishbutton is clicked, the
onSavefunction of the current page is called, and the navigation action is only performed if the current page's
Viewis completed by default, that's why we can navigate to page number two without completing page one first. Let's change that.
BasicDataeditor, we override the
onSavefunction to perform a partial commit of the
typefields, because that's the only two fields the user can change on that page.
onSavefunction in the Wizard itself is activated. If the Wizard's
completedstate is true after the
onSavecall, the wizard dialog is closed, provided that the user calls
super.onSave(). In such a scenario, the Wizard itself needs to handle whatever should happen in the
onSavefunction. Another possibility is to configure a callback that will be executed whenever the wizard is completed. With that approach, we need access the completed customer object somehow, so we inject it into the wizard itself as well:
onCompletecallback that extracts the customer and inserts it into a database before it opens the newly created Customer object in a CustomerEditor View:
CustomerModel. This model is injected into both pages, so it should be the same instance. But what if other parts of the application is already using the
CustomerModelin the same scope we created the Wizard from? It turns out that this is not even an issue, because the
Wizardbase class implements
InjectionScopedwhich makes sure that whenever you inject a
Wizardsubclass, a new scope is automatically activated. This makes sure that whatever resources we require inside the Wizard will be unique and not shared with any other part of the application.
Nextbutton was enabled whenever there was another page to navigate forward to. The
Finishbutton was also always enabled. This might be fine, but you can improve the cues given to your users by only enabling those buttons when it would make sense to click them. By looking into the
Wizardbase class, we can see that the buttons are bound to the following boolean expressions:
canFinishexpression is bound to the
Finishbutton and the
canGoNextexpression is bound to the
Wizardclass also includes some boolean expressions that are unused by default. Two of those are
allPagesComplete. These expressions are always up to date, and we can use them in our
CustomerWizardto improve the user experience.
Finishbuttons will only be enabled whenever the new conditions are met. This is what we want, but we're not done yet. Remember how we only updated
onSavewas called? You might also remember that
onSavewas called whenever
Finishwas clicked? It looks like we have ourselves a good old Catch22 situation here, folks!
autocommitparameter to each binding in our ViewModel:
onSavecallback, but we do need to redefine the
completeboolean expression in each wizard page.
onSavefunctions as we no longer need them. If you run the application with these changes you will see how much more expressive the Wizard becomes in terms of telling the user when he can proceed and when he can finish the process. Using this approach will also convey that any non-filled data is optional once the
Finishbutton is enabled.
falseto remove the steps view completely
trueto turn each step description into a hyperlink
falseto no longer require that the current page is valid before navigating to the new page
trueto add the index number before each step description
falseto remove the header
Wizardclass is a
BorderPane. The header will be in the
topslot, the steps are in the
leftslot, the pages are in the
centerslot and the buttons are in the
bottomslot. You can change/hide/add styling and set properties to these nodes as you see fit to alter the design and layout of the Wizard. A good place to do this would be in the
onDockcallback of your wizard subclass. It is completely valid change the layout in any way you see fit, you can even remove the
BorderPaneand move the other parts into another layout container for example.