ZK8 Wizard Example - Part 1"

From Documentation
m (correct highlight (via JWB))
 
(20 intermediate revisions by one other user not shown)
Line 1: Line 1:
 
{{Template:Smalltalk_Author|
 
{{Template:Smalltalk_Author|
 
|author=Robert Wenzel, Engineer, Potix Corporation
 
|author=Robert Wenzel, Engineer, Potix Corporation
|date=July/August 2015
+
|date=September 2015
 
|version=ZK 8.0
 
|version=ZK 8.0
 
}}
 
}}
Line 8: Line 8:
  
 
ZK 8 contains several new features to provide an easier approach to cleaner MVVM development and simplify reusing UI elements.
 
ZK 8 contains several new features to provide an easier approach to cleaner MVVM development and simplify reusing UI elements.
The goal of this smalltalk series is to give ideas and scenarios where/how those new features (e.g. '''LINK ME''' shadow elements and template injection)  
+
The goal of this smalltalk series is to give ideas and scenarios where/how those new features (mainly [[Small_Talks/2015/May/New_Features_of_ZK_8.0.0_RC#Shadow_Elements|shadow elements]] allowing more flexible template control)  
can be applied, and how it helps to separate the application model (java code) from the UI (zul files).  
+
can be applied, and how they help to separate the application model (java code) from the UI (zul files).  
  
e.g. <sh:if> <sh:forEach> <sh:apply> <sh:choose>
+
The new shadow elements are basic control structures such as <sh:if> <sh:forEach> <sh:apply> <sh:choose> with embedded support for data binding and automatic updates caused by changes in the model.
 
 
This example is '''NOT''' intended to be copied/pasted as-is, as every application has different requirements and here only those requirements that I made up myself are covered. So please don't claim missing features in this wizard (according to my requirements they are not missing) and if you like them feel free to adapt these ideas to your own requirements.
 
  
 
As an example I chose a reusable wizard template. A wizard typically has the same frame layout but different content for each step - making it an ideal showcase for templating and a model driven UI (MVVM).
 
As an example I chose a reusable wizard template. A wizard typically has the same frame layout but different content for each step - making it an ideal showcase for templating and a model driven UI (MVVM).
  
 
There will be 4 Parts in this series:
 
There will be 4 Parts in this series:
# Defining a wizard template (+ simple Survey example)
+
*[[Small Talks/2015/September/ZK8_Wizard_Example_-_Part_1|Part 1 - Defining the Wizard]] (You are here)
# Using the wizard in a complex example (Order process)
+
[[File:wizardexample-part1-shot.png|200px]]
# Add form binding & validation
+
*[[Small Talks/2015/September/ZK8_Wizard_Example_-_Part_2|Part 2 - Order Wizard (a more complex example)]]
# Styling the wizard (with Bootstrap)
+
[[File:wizardexample-part2-shot.png|200px]]
 +
*[[Small Talks/2016/February/ZK8_Wizard_Example_-_Part_3|Part 3 - Form Handling and Input Validation]]
 +
[[File:wizardexample-part3-shot.png|200px]]
 +
*[[Small_Talks/2016/April/ZK8_Wizard_Example_-_Part_4_final|Part 4 - Styling the wizard (with Bootstrap)]]
 +
[[File:wizardexample-part4-shot.png|400px]]
 +
 
 +
This example is '''NOT''' intended to be copied/pasted as-is, as every application has different requirements and here only those requirements that I made up myself are covered. So please don't claim missing features in this wizard (according to my requirements they are not missing) and if you like them feel free to adapt these ideas to your own requirements.
  
 
Note that I am using Java 8 for this example (everything can be done in older Java versions too, it's often just more to write).
 
Note that I am using Java 8 for this example (everything can be done in older Java versions too, it's often just more to write).
Line 27: Line 31:
 
If you find my usage of Java 8 features inappropriate I am happy about constructive comments.
 
If you find my usage of Java 8 features inappropriate I am happy about constructive comments.
  
This first part will introduce a wizard template with a simple example wizard, implementing a 3-Step Survey.
+
In this first part I will introduce a wizard template with a simple example wizard, implementing a 3-Step Survey.
  
 
= Part 1 - Define the Wizard zul template and model classes =
 
= Part 1 - Define the Wizard zul template and model classes =
  
The Wizard consists of 3 files the WizardViewModel class with its WizardStep(s), and a zul-template (wizard.zul) rendering the Wizard according to the WizardViewModel's state.
+
The Wizard template consists of 3 files the WizardViewModel class with its WizardStep(s), and a zul-template (wizard.zul) rendering the Wizard according to the WizardViewModel's state.
  
 
To use the template in a zul file an instance of WizardViewModel needs to be passed with the parameter "wizardModel" using the <sh:apply> shadow element.
 
To use the template in a zul file an instance of WizardViewModel needs to be passed with the parameter "wizardModel" using the <sh:apply> shadow element.
Line 51: Line 55:
 
== Zul Template ==
 
== Zul Template ==
  
;/src/main/webapp/WEB-INF/zul/template/wizard/wizard.zul
+
;/src/main/webapp/WEB-INF/zul/template/wizard/wizard.zul [https://github.com/cor3000/zk-wizard-example/blob/part-1/src/main/webapp/WEB-INF/zul/template/wizard/wizard.zul]
 
:renders the wizard, based on the current step information
 
:renders the wizard, based on the current step information
  
<source lang="xml" high="1, 6, 11, 12, 13">
+
<source lang="xml" highlight="1, 6, 11, 12, 13">
 
<zk xmlns:sh="shadow">
 
<zk xmlns:sh="shadow">
 
<window border="normal" title="@load(wizardVM.currentStep.title)"
 
<window border="normal" title="@load(wizardVM.currentStep.title)"
Line 93: Line 97:
 
== Java (View)Model ==
 
== Java (View)Model ==
  
;zk.example.wizard.model.WizardViewModel
+
;zk.example.wizard.model.WizardViewModel [https://github.com/cor3000/zk-wizard-example/blob/part-1/src/main/java/zk/example/wizard/model/WizardViewModel.java]
 
:keeps the wizard state (available steps, current step, progress...) used as the MVVM view model inside the wizard.zul template
 
:keeps the wizard state (available steps, current step, progress...) used as the MVVM view model inside the wizard.zul template
  
<source lang="java" high="9, 12, 15, 17">
+
<source lang="java" highlight="9, 12, 15, 17">
 
public class WizardViewModel<T extends WizardStep> {
 
public class WizardViewModel<T extends WizardStep> {
 
...
 
...
Line 121: Line 125:
  
  
;zk.example.wizard.model.WizardStep
+
;zk.example.wizard.model.WizardStep [https://github.com/cor3000/zk-wizard-example/blob/part-1/src/main/java/zk/example/wizard/model/WizardStep.java]
 
:contains the information for a single step (id, zul page, title, beforeNext callback, otherwise nothing special)
 
:contains the information for a single step (id, zul page, title, beforeNext callback, otherwise nothing special)
  
<source lang="java" high="">
+
<source lang="java" highlight="">
 
public class WizardStep {
 
public class WizardStep {
 
private String id;
 
private String id;
Line 141: Line 145:
 
* step pages (/WEB-INF/zul/survey/steps/*.zul)
 
* step pages (/WEB-INF/zul/survey/steps/*.zul)
  
;zk.example.survey.SurveyViewModel
+
 
 +
;zk.example.survey.SurveyViewModel [https://github.com/cor3000/zk-wizard-example/blob/part-1/src/main/java/zk/example/survey/SurveyViewModel.java]
 
:The view model class containing the initialization code, defining the steps
 
:The view model class containing the initialization code, defining the steps
  
<source lang="java" high="4, 5, 14, 15, 18">
+
<source lang="java" highlight="4, 5, 14, 15, 18">
 
...
 
...
 
@Init
 
@Init
Line 178: Line 183:
 
(As an improvement I leave it to the reader to implement a true builder pattern of the Steps so that the view model doesn't have to interact with the WizardStep class directly.)
 
(As an improvement I leave it to the reader to implement a true builder pattern of the Steps so that the view model doesn't have to interact with the WizardStep class directly.)
  
;/src/main/webapp/survey.zul
+
;/src/main/webapp/survey.zul [https://github.com/cor3000/zk-wizard-example/blob/part-1/src/main/webapp/survey.zul]
 
:the start page using the simple wizard
 
:the start page using the simple wizard
  
<source lang="xml" high="1, 7, 10">
+
<source lang="xml" highlight="1, 7, 10">
 
<?component name="wizard" templateURI="/WEB-INF/zul/template/wizard/wizard.zul" ?>
 
<?component name="wizard" templateURI="/WEB-INF/zul/template/wizard/wizard.zul" ?>
 
<zk>
 
<zk>
Line 205: Line 210:
 
* '''Line 10:''' inject a template re-usable throughout the wizard steps
 
* '''Line 10:''' inject a template re-usable throughout the wizard steps
  
;/src/main/webapp/WEB-INF/zul/survey/steps
+
;/src/main/webapp/WEB-INF/zul/survey/steps [https://github.com/cor3000/zk-wizard-example/tree/part-1/src/main/webapp/WEB-INF/zul/survey/steps]
:contains all the wizard steps - showing one exemplary step here: '''question_2.zul'''
+
:contains all the wizard steps - showing one exemplary step here: '''question_2.zul''' [https://github.com/cor3000/zk-wizard-example/blob/part-1/src/main/webapp/WEB-INF/zul/survey/steps/question_2.zul]
  
<source lang="xml" high="3, 4, 6">
+
<source lang="xml" highlight="3, 4, 6">
 
<zk xmlns:sh="shadow">
 
<zk xmlns:sh="shadow">
 
Have you ever climbed a rock?
 
Have you ever climbed a rock?
Line 221: Line 226:
  
 
== Class diagram ==
 
== Class diagram ==
 +
To illustrate the class relationships here a small diagram:
  
 
[[File:survey_wizard_class_dia.png]]
 
[[File:survey_wizard_class_dia.png]]
Line 226: Line 232:
 
We can see there is a clear separation between the application model (Survey) and the wizard specific classes.
 
We can see there is a clear separation between the application model (Survey) and the wizard specific classes.
 
This means:
 
This means:
* the Wizard does not contain any Survey specific code so it can be used for a different purpose. (see part  2 '''LINK ME''')
+
* the Wizard does not contain any Survey specific code so it can be used for a different purpose. (see [[Small_Talks/2015/September/ZK8_Wizard_Example_-_Part_2|Part 2]])
* the Survey isn't aware of being filled out in a wizard so it can also be presented in a different way (e.g. in a tabbox or just all question on a single page).
+
* the Survey isn't aware of being filled out in a wizard so it could also be presented in a different way (e.g. in a tabbox or just all question on a single page - not part of this example).
* the SurveyViewModel fits things together by initializing the wizardModel with steps and the Survey, it also handles to the last step to restart the wizard
+
* the SurveyViewModel fits things together by initializing the Wizard Model with steps and the Survey object, it also handles to the last step callback to restart the wizard
  
 
= Summary =
 
= Summary =
  
As you see the shadow elements come in handy avoiding code duplication in the view and ensure a consistent rendering. On the java side the class boundaries are designed to avoid mixing their purpose while the providing all the information required to control what is rendered and how, driven by the model classes (The zul files contain as little logic as possible, just following the view model's state)
+
As you see the shadow elements come in handy avoiding code duplication in the view and ensure a consistent rendering. On the java side the class boundaries are designed to avoid mixing their purpose while the providing all the information required to control what is rendered and how. All driven by the model classes (the zul files contain as little logic as possible, just following the view model's state).
 
That's enough to create a wizard for a simple case.
 
That's enough to create a wizard for a simple case.
  
Now let's use the same wizard template/view model for a more complex case. in Part 2 '''LINK ME'''
+
Now let's use the same wizard template/view model for a more complex case in [[Small_Talks/2015/September/ZK8_Wizard_Example_-_Part_2|Part 2]].
  
 
== Download ==
 
== Download ==
* The source code for this article can be found in [https://github.com/cor3000/zk-wizard-example/tree/part-1 github (tag: part-1)].
+
* The source code for this article can be found in [https://github.com/cor3000/zk-wizard-example/tree/part-1 github (branch: part-1)].
  
 
== Running the Example ==
 
== Running the Example ==
Checkout the tag '''part-1'''
+
Clone the repository and Checkout '''part-1'''
  
 +
    git clone [email protected]:cor3000/zk-wizard-example.git
 +
    cd zk-wizard-example
 
     git checkout part-1
 
     git checkout part-1
  

Latest revision as of 04:21, 20 January 2022

DocumentationSmall Talks2015SeptemberZK8 Wizard Example - Part 1
ZK8 Wizard Example - Part 1

Author
Robert Wenzel, Engineer, Potix Corporation
Date
September 2015
Version
ZK 8.0

Introduction

ZK 8 contains several new features to provide an easier approach to cleaner MVVM development and simplify reusing UI elements. The goal of this smalltalk series is to give ideas and scenarios where/how those new features (mainly shadow elements allowing more flexible template control) can be applied, and how they help to separate the application model (java code) from the UI (zul files).

The new shadow elements are basic control structures such as <sh:if> <sh:forEach> <sh:apply> <sh:choose> with embedded support for data binding and automatic updates caused by changes in the model.

As an example I chose a reusable wizard template. A wizard typically has the same frame layout but different content for each step - making it an ideal showcase for templating and a model driven UI (MVVM).

There will be 4 Parts in this series:

Wizardexample-part1-shot.png

Wizardexample-part2-shot.png

Wizardexample-part3-shot.png

Wizardexample-part4-shot.png

This example is NOT intended to be copied/pasted as-is, as every application has different requirements and here only those requirements that I made up myself are covered. So please don't claim missing features in this wizard (according to my requirements they are not missing) and if you like them feel free to adapt these ideas to your own requirements.

Note that I am using Java 8 for this example (everything can be done in older Java versions too, it's often just more to write). I encourage everyone to switch to Java 8 to benefit from new language features and more important: security updates. If you find my usage of Java 8 features inappropriate I am happy about constructive comments.

In this first part I will introduce a wizard template with a simple example wizard, implementing a 3-Step Survey.

Part 1 - Define the Wizard zul template and model classes

The Wizard template consists of 3 files the WizardViewModel class with its WizardStep(s), and a zul-template (wizard.zul) rendering the Wizard according to the WizardViewModel's state.

To use the template in a zul file an instance of WizardViewModel needs to be passed with the parameter "wizardModel" using the <sh:apply> shadow element.

...
    <sh:apply templateURI="/WEB-INF/zul/template/wizard/wizard.zul" wizardModel="@init(vm.wizardModel)" />
...

An equivalent syntax alternative is to define a custom component referencing the templateURI.

<?component name="wizard" templateURI="/WEB-INF/zul/template/wizard/wizard.zul" ?>
...
    <wizard wizardModel="@init(vm.wizardModel)" />
...

Zul Template

/src/main/webapp/WEB-INF/zul/template/wizard/wizard.zul [1]
renders the wizard, based on the current step information
<zk xmlns:sh="shadow">
	<window border="normal" title="@load(wizardVM.currentStep.title)"
			viewModel="@id('wizardVM') @init(wizardModel)"
			validationMessages="@id('vmsgs')"
			onOK="@command(wizardVM.nextCommand)">
		<sh:apply template="wizardContent"/>
	</window>

	<template name="wizardContent">
		<vlayout width="100%">
			<sh:apply template="wizardProgress"/>
			<sh:apply templateURI="@load(wizardVM.currentStepTemplateUri)" />
			<sh:apply template="wizardButtons"/>
		</vlayout>
	</template>
	
	<template name="wizardProgress">
		<progressmeter value="@load(wizardVM.progress)" width="100%" />
	</template>
	
	<template name="wizardButtons">
		<hlayout>
			<sh:if test="@load(wizardVM.backVisible)">
				<button label="@load(wizardVM.backLabel)" onClick="@command(wizardVM.backCommand)" />
			</sh:if>
			<sh:if test="@load(wizardVM.nextVisible)">
				<button label="@load(wizardVM.nextLabel)" onClick="@command(wizardVM.nextCommand)" />
			</sh:if>
		</hlayout>
	</template>
</zk>
  • Line 1: declare the shadow namespace
  • Lines 6, 11, 13: statically invoke templates to separate the ui elements (optional, could all be inline)
  • Line 12: insert an anonymous template by URI to a zul page

Java (View)Model

zk.example.wizard.model.WizardViewModel [2]
keeps the wizard state (available steps, current step, progress...) used as the MVVM view model inside the wizard.zul template
public class WizardViewModel<T extends WizardStep> {
	...
	private T currentStep;
	private List<T> availableSteps;
	...
	public WizardViewModel(List<T> availableSteps) {...}

	@Command(BACK_COMMAND) 
	public void back() {...}
	
	@Command(NEXT_COMMAND)
	public void next() {...}

	//callback hook to intercept different steps
	protected void onStepChanged(T currentStep) {}

	public boolean gotoStep(String stepId) {...}
	...
	/*various getters dependent on the current step*/
  • Lines 9, 12, 17: methods to navigate through the wizard (next/back are also used as command handlers for buttons)
  • Line 15: callback hook method to be notified about wizard step changes (used in Part 2)


zk.example.wizard.model.WizardStep [3]
contains the information for a single step (id, zul page, title, beforeNext callback, otherwise nothing special)
public class WizardStep {
	private String id;
	private String title;
	private String templateUri;
	private String nextLabel;
	private Runnable beforeNext = () -> {};
	...

Basic Usage example (a simple Survey)

The basic wizard usage can be demonstrated implementing a simple survey consisting of following parts:

  • Java ViewModel (SurveyViewModel.java)
  • start page (survey.zul)
  • step pages (/WEB-INF/zul/survey/steps/*.zul)


zk.example.survey.SurveyViewModel [4]
The view model class containing the initialization code, defining the steps
	...
	@Init
	public void init() {
		survey = new Survey();
		initWizardModel();
	}

	private void initWizardModel() {
		List<WizardStep> availableSteps = Arrays.asList(
				wizardStep("question_1"),
				wizardStep("question_2"),
				wizardStep("question_3"),
				wizardStep("done")
					.withBeforeNextHandler(() -> Executions.sendRedirect("./survey.zul"))
					.withNextLabel("Restart")
				);

		wizardModel = new WizardViewModel<WizardStep>(availableSteps);
	}

	private WizardStep wizardStep(String stepId) {
		String title = stepId;
		String templateUri = STEP_FOLDER + stepId + ".zul";
		return new WizardStep(stepId, title, templateUri);
	}
	...
  • Lines 3, 4, 18: initialize the viewModel properties
  • Lines 14, 15: define custom "next" handling and label

This ViewModel is not doing much, just creating the empty Survey and WizardStep objects and finally initializing the WizardViewModel. (As an improvement I leave it to the reader to implement a true builder pattern of the Steps so that the view model doesn't have to interact with the WizardStep class directly.)

/src/main/webapp/survey.zul [5]
the start page using the simple wizard
<?component name="wizard" templateURI="/WEB-INF/zul/template/wizard/wizard.zul" ?>
<zk>
	<div width="500px" 
		 viewModel="@id('vm') @init('zk.example.simplewizard.SimpleWizardViewModel')" 
		 validationMessages="@id('vmsgs')">

		<wizard wizardModel="@init(vm.wizardModel)" survey="@init(vm.survey)">
	
			<!-- injected template - visible inside the wizard -->
			<template name="yesno">
				<radiogroup selectedItem="@bind(answer)">
					<radio label="Yes" value="${true}"/>
					<radio label="No" value="${false}"/>
				</radiogroup>
			</template>	
		</wizard>
	</div>
</zk>
  • Line 1: define the <wizard> component based on a template
  • Line 7: add a <wizard> using a wizardModel and a survey object
  • Line 10: inject a template re-usable throughout the wizard steps
/src/main/webapp/WEB-INF/zul/survey/steps [6]
contains all the wizard steps - showing one exemplary step here: question_2.zul [7]
<zk xmlns:sh="shadow">
	Have you ever climbed a rock?
	<sh:apply template="yesno" answer="@ref(survey.answer2)"/>
	<sh:if test="@load(survey.answer2)">
		Did you fall down?
		<sh:apply template="yesno" answer="@ref(survey.answer2b)"/>
	</sh:if>
</zk>
  • Lines 3, 6: apply the previously injected template
  • Line 4: conditionally display a dependent question

Class diagram

To illustrate the class relationships here a small diagram:

Survey wizard class dia.png

We can see there is a clear separation between the application model (Survey) and the wizard specific classes. This means:

  • the Wizard does not contain any Survey specific code so it can be used for a different purpose. (see Part 2)
  • the Survey isn't aware of being filled out in a wizard so it could also be presented in a different way (e.g. in a tabbox or just all question on a single page - not part of this example).
  • the SurveyViewModel fits things together by initializing the Wizard Model with steps and the Survey object, it also handles to the last step callback to restart the wizard

Summary

As you see the shadow elements come in handy avoiding code duplication in the view and ensure a consistent rendering. On the java side the class boundaries are designed to avoid mixing their purpose while the providing all the information required to control what is rendered and how. All driven by the model classes (the zul files contain as little logic as possible, just following the view model's state). That's enough to create a wizard for a simple case.

Now let's use the same wizard template/view model for a more complex case in Part 2.

Download

Running the Example

Clone the repository and Checkout part-1

   git clone [email protected]:cor3000/zk-wizard-example.git
   cd zk-wizard-example
   git checkout part-1

The example war file can be built with maven:

   mvn clean package

Execute using jetty:

   mvn jetty:run

Then access the overview page http://localhost:8080/wizardexample/survey.zul

And that's what you'll see:


Comments



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.