Template Examples - Toggle Switch

From Documentation
DocumentationSmall Talks2017JuneTemplate Examples - Toggle Switch
Template Examples - Toggle Switch

Author
Robert Wenzel, Engineer, Potix Corporation
Date
June 1, 2017
Version
ZK 8.0

Styling example adapted from: http://callmenick.com/_development/css-toggle-switch/

Introduction

The web is full of html5/css based layout examples ... e.g. this: css-toggle-switch-examples

Template-examples-css-toggle-switch.gif

The author of the example explains in detail how the styles work so I'll not repeat that here and focus instead on how to integrate this or similar examples into a ZK application. This article tries to guide through simple and more advanced approaches. To start with the simple one first:

Using the original markup and styles

A very basic way to integrate a 3rd party layout is to re-use the markup as-is. Here the original source html example.

	<div class="settings">
		<div class="row">
			<div class="switch">
				<input id="cmn-toggle-1" class="cmn-toggle cmn-toggle-round" type="checkbox">
				<label for="cmn-toggle-1"></label>
			</div>
		</div>
...
	</div>

ZK's xhtml component set can be combined with ZUL components - using the xhtml namespace. (Note: the xhtml namespace requires an optional jar file - zhtml.jar - which needs to be added to the maven/gradle dependencies)

cmn-toggle-original.zul
<zk xmlns:x="xhtml">
	<div sclass="settings"><!-- zul-div -->
		<x:div class="row"><!-- xhtml-div -->
			<x:div class="question" textContent="Enable Cookies" />
			<x:div class="switch">
				<x:input id="cmn-toggle-1" class="cmn-toggle cmn-toggle-round" type="checkbox" checked="true" />
				<x:label for="cmn-toggle-1" />
			</x:div>
		</x:div>

		<x:div class="row">
			<x:div class="question" textContent="Share Location" />
			<x:div class="switch">
				<x:input id="cmn-toggle-2" class="cmn-toggle cmn-toggle-round-flat" type="checkbox" />
				<x:label for="cmn-toggle-2" />
			</x:div>
		</x:div>
...
	</div>
</zk>

There you are. Layout integrated!?

Read on there's more.

Streamlining with templates

I assume there were no surprises in the previous paragraph, it only looks a bit repetitive. Let's make our lives a little easier writing less code using a template, for the actual toggle switch.

template/cmn-toggle-template-xhtml.zul
<zk xmlns:x="xhtml">
	<x:div class="switch">
		<x:input class="cmn-toggle cmn-toggle-${toggleClass}" type="checkbox" checked="${checked}" /><!-- no whitespace
	 --><x:label for="${self.previousSibling.uuid}" data-on="${labelOn}" data-off="${labelOff}" />
	</x:div>
</zk></zk>
  • Line 3: Avoiding the whitespace to keep both nodes direct siblings - otherwise there would be a Text containing whitespace between them.

As xhtml elements create a server side component for each element this can be further optimized using native elements (of the native namespace) for the static surrounding div element and label. Only the <input>-element remains a xhtml component which is available at server side because we might be interested in it's checked/unchecked state.

template/cmn-toggle-template.zul
<zk xmlns:x="xhtml" xmlns:n="native">
	<n:div class="switch">
		<x:input class="cmn-toggle cmn-toggle-${toggleClass}" type="checkbox" checked="${checked}"/>
		<n:label for="${self.firstChild.uuid}" data-on="${labelOn}" data-off="${labelOff}" />
	</n:div>
</zk>
  • Line 4: Seems weird at first: Since the native elements are optimized by joining them into a single component, the <n:label> gets combined with the surrounding <n:div>, leaving the input the firstChild of the combined surrounding element (just a little trick to avoid having to hard code IDs.)

Once we have a template we can declare and use it in our page layout as often as needed. While both templates will produce the same DOM elements, the second one will be more efficient and consume less memory (at the cost of being mainly static).

cmn-toggle-original-with-template.zul
<?component name="toggle" templateURI="template/cmn-toggle-template-xhtml.zul"?>

<zk xmlns:x="xhtml">
	<div sclass="settings">
		<x:div class="row">
			<x:div class="question" textContent="Enable Cookies"/>
			<toggle toggleClass="round" checked="true"/>
		</x:div>
	
		<x:div class="row">
			<x:div class="question" textContent="Share Location"/>
			<toggle toggleClass="round-flat" />
		</x:div>
...
	</div>

Defined as an inline template, the "settingsRow" can be applied multiple times with different parameters to save a few more lines of code:

cmn-toggle-original-with-template-even-less.zul
<?component name="toggle" templateURI="template/cmn-toggle-template.zul"?>

<zk xmlns:x="xhtml" xmlns:n="native">
	<div sclass="settings">
		<apply template="settingsRow" question="Enable Cookies" toggleClass="round" checked="true"/>
		<apply template="settingsRow" question="Share Location" toggleClass="round-flat"/>
...
		
		<template name="settingsRow">
			<n:div class="row">
				<n:div class="question">${question}</n:div>
				<toggle/><!-- inherits the parameters passed into the settingsRow template -->
			</n:div>
		</template>
	</div>
</zk>

It won't get much shorter ... or does it?

Customize ZK's <checkbox> component

In this case we are lucky: the generated HTML markup structure for a ZK checkbox almost matches the css styles provided by the cmn-toggle example. With a few adjustments ZK's default checkbox can look the same and provide almost all features (except for the dynamic yes/no labels).

Original HTML suggested by cmn-toggle:

	<div class="switch">
		<input id="cmn-toggle-1" class="cmn-toggle cmn-toggle-round" type="checkbox">
		<label for="cmn-toggle-1"></label>
	</div>
  • Line 1: same DOM structure mentioned above

By default ZK will generate this HTML for a checkbox:

	<span id="i8iY9" class="z-checkbox">
		<input type="checkbox" id="i8iY9-real">
		<label for="i8iY9-real" id="i8iY9-cnt" class="z-checkbox-content"></label>
	</span>

After adding a zclass and sclass ...

	<checkbox zclass="cmn-toggle" sclass="switch cmn-toggle-round"/>

... ZK will generate the following HTML elements:

	<span id="nECY2" class="switch cmn-toggle-round cmn-toggle">
		<input type="checkbox" id="nECY2-real" checked="checked">
		<label for="nECY2-real" id="nECY2-cnt" class="cmn-toggle-content"></label>
	</span>

Notice how the style class cmn-toggle moved from input element to the surrounding span element.

Because of that the original cmn-toggle styles require a little adjustment to fit this structure (resulting in cmn-toggle-for-zk-checkbox.css). As this is not a CSS tutorial so I'll spare you the details. Below just a simple example of what kind of changes are needed:

css/cmn-toggle-original.css
.cmn-toggle {
  position: absolute;
  margin-left: -9999px;
  visibility: hidden;
}
.cmn-toggle + label {
  display: block;
  position: relative;
...
css/cmn-toggle-for-zk-checkbox.css
.cmn-toggle > input {
  position: absolute;
  margin-left: -9999px;
  visibility: hidden;
}
.cmn-toggle > label {
  display: block;
  position: relative;
...

Most changes reuse the existing styles and only the css-selectors are changed to reflect which DOM elements actually have the style classes.

Now we can build the same UI even without an additional external template as cmn-toggle-template.zul. Still the inline template helps to avoid repeating the layout markup within the our page.

<zk xmlns:n="native">
	<div sclass="settings">
		<apply template="settingsRow" question="Enable Cookies" toggleClass="round" checked="true"/>
		<apply template="settingsRow" question="Share Location" toggleClass="round-flat"/>
		<apply template="settingsRow" question="Do you agree?" toggleClass="yes-no" />
		<apply template="settingsRow" question="Like it?" toggleClass="yes-no large-icon thumbs" checked="true"/>
		
		<template name="settingsRow">
			<n:div class="row">
				<n:div class="question">${question}</n:div>
				<checkbox zclass="cmn-toggle" sclass="switch cmn-toggle-${toggleClass}" checked="${checked}"/>
			</n:div>
		</template>
	</div>
</zk>

Without changing ZK's markup structure we can't use the data-on/-off attributes (which define alternative labels or font icons) directly. Instead we can define additional css classes (e.g. the thumbs and large-icon class) to specify a different content within our CSS.

.cmn-toggle-yes-no.large-icon > label:before,
.cmn-toggle-yes-no.large-icon > label:after {
  font-family: FontAwesome;
  font-size: 50px;
}

.cmn-toggle-yes-no.thumbs > label:before {
  content: '\f088';
}
.cmn-toggle-yes-no.thumbs > label:after {
  content: '\f087';
}

The codes '\f087' and '\f088' represent the character codes of the font-awesome icons thumbs-o-up and thumbs-o-down

If that's too limiting it is always possible to implement a custom mold to render the ZK checkbox using the same markup as required by the cmn-toggle-switch (If you are interested in this please let me know in the comments.)

Summary

As demonstrated above there are many options in ZK you can choose from depending on your requirements, personal preference or experience. I hope you enjoy this little smalltalk and I wish you happy styling.

Example Sources

The code examples are available on github in the zk-template-examples repository

files: https://github.com/zkoss-demo/zk-template-examples/tree/toggle-switch/src/main/webapp/css-toggle-switch

Running the Example

Clone the repo

   git clone git@github.com:zkoss-demo/zk-template-examples.git

The example war file can be built using the gradle-wrapper (on windows simply omit the prefix './'):

   ./gradlew war

Execute using jetty:

   ./gradlew appRun

Then access the example http://localhost:8080/zk-template-examples/css-toggle-switch/


Comments



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