Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

3 Steps to Compose a Lightbox With Standard ZK Components

DZone's Guide to

3 Steps to Compose a Lightbox With Standard ZK Components

In this post, we discuss how to build a simple lightbox, a popup that displays various panels with navigation arrows for better UI.

· Web Dev Zone ·
Free Resource

Bugsnag monitors application stability, so you can make data-driven decisions on whether you should be building new features, or fixing bugs. Learn more.

What’s Going On?

Today we are going on a step-by-step tour in the world of composite controls. We will discuss how to build a simple lightbox — a popup displaying various panels with navigation arrows.

Lightbox model

The full runnable example can be found in this GitHub repository.

But Why, Though?

We’ve all been there. We need to put a control in our UI, and that control currently doesn’t exist. Somewhere, someone is breathing down our neck with helpful comments such as “but [insert unrelated language/framework/kitchen appliance] have that feature! Therefore, we should have it too!”

Well, time to code. But let’s code smart. We don’t need to get to the bottom of the tech stack here. We just want to show some pictures in a nice box, not rewrite the whole HTML standard.

So Then, What to Do?

When working with a mature language or framework, there is usually building blocks lying around. On their own, they may be unassuming, but they can be used to generate our ideal control. By combining existing features into a reusable control, we can simplify future designs and rationalize the structure of our application.

For Real, What Are We Doing Right Now?

Well, we got a feature request. Our UI needs lightboxes. Basically, a nice-looking popup window which embeds a slice of content and can switch between views with arrows.

Here’s the thing, we are running a ZK application, therefore, we have access to a bunch of ZK components. Those components are automated controls to use when building our UI. They can be as simple as an input text field, and as complex as a full grid view.

Great, Then Let’s Use a ZK Lightbox and Get This Over

Good plan, only one tiny problem with it though. There is no lightbox in ZK. The solution is easy, let’s make one.

That Sounds Suspiciously Like That QR Code Integration Thing From Before

Not really. Last time we talked, we integrated an external library to do something new with the framework. This time is much simpler. We already have everything we need in the framework. We’ve got windows and buttons and everything else. We just need to put it all together.

Let’s Go, Then. Where Do We Start?

The same place every time. We make a prototype inline in a page. We want to make sure it looks good and feels nice to use before we make it reusable, right? So, let’s see; here’s a rough idea of what we are going for.

Lighbox concept diagram


Let’s see what we are going for. We want the lightbox to act as a modal container, which is a container floating in the foreground of our page. In this window, we will need three different elements. On each side of the window, we want a clickable arrow to switch between views. In the center, we will display the current view.

When the left arrow is clicked, we will switch the previous tile. When the right arrow is clicked, we will switch to the next tile.

So Far So Good. Time to Code?

Absolutely. Since we are making our prototype in ZK, let’s start with a zul page, which is a simple way to create a component hierarchy in ZK.

 Since we want to get something running without too much customization, let’s see which ZK components could be used for each part of our control.

Building it Step-by-Step

For the container, let’s go with a window ZK component. It comes out of the box with all the features we will need for this purpose. In this case, we want to use the modal mode, the title, and the close button.

Now for the arrows. Since we are going to click on them, the obvious choice would be to use buttons. However, we are going to choose some 'A' elements — or XHTML link elements — instead. XHTML elements are regular HTML tags that behave as ZK components. They act basically as regular HTML <a> tag, but with ZK workflow and events. Since they are ZK components, we can define onClick listeners in our server-side code, and let the framework take care of the rest. Plus, they can be styled with font-awesome icons, which we will use to have some nice Unicode arrow icons.

Finally, the content tile. Here we have an interesting choice to make. We could use a simple apply shadow element or an include component to just insert content from arbitrary sources, or we could use a cardlayout component, which comes with tile transitions and can receive content directly as children.

For the prototype phase, let’s choose the easiest way. We will put a Cardlayout with some test panels inline directly as children. In the next step, we will use a model-based approach to make the control even more reusable, but that’s a bit too early for that yet.

Step 1 - Prototype

First, the window. We are going to use three attributes.

Mode="Modal" - The window will display as a floating popup, with a grey-out mask hiding the original page behind.

Title="Lightbox prototype" - Will display a title bar above the content

Closable="true" - Will display a close button to remove the window and go back to the original page.

<window mode="modal" title="Lightbox prototype" closable="true">

We will slide in a div inside of the window to act as a container for the rest of the UI objects.

Here, we only add the sclass attribute to act as a relatively positioned container. We will talk more about this div during the styling process.

sclass="content-wrapper" - This div will be rendered on the client-side with an additional class, content-wrapper. We will use this class to define the resulting <div> HTML element as position:relative. This will let the arrows position themselves based on the dimensions of the wrapper, without having to worry about the surrounding window.

<div sclass="content-wrapper">

Let’s get to the main event. We will add a card layout to our structure. This component will receive an id and dimensions.

id="card" - This is useful during the prototype phase. Other components can trigger methods or getters on the cardlayout element simply by referring to this id.

width="300px" height="200px" - Dimensions of the cardlayout element.

<cardlayout id="card" width="300px" height="200px">

Finally, we need a bit of content to test our card layout, so let’s just throw in a few divs with some color backgrounds and placeholder text.

height="100%" width="100%" - Use all of the available space in the parent component.

style="background-color:steelblue;padding:20px" - standard inline CSS applied to this element.

<div height="100%" width="100%" >

card 1 content

</div>

Alright, let's add a bit of CSS to position the arrows in the right place and see how it snaps together.

<zk>
<style>
.lightbox-arrow-right {
    /* align right arrow to right border */
    right: 0px;
    text-align: right;
}
.lightbox-arrow-left, .lightbox-arrow-right {
width:30px;
height: 30px;
    position: absolute;
    /* display in front of everything */
    z-index: 999;
    /* top minus half of the arrow box height*/
    top: calc(50% - 15px);
    /* unicode arrows: font-size for bigger arrows */
    font-size: 30px;
    text-decoration: none;
}
.content-wrapper{
/* used as the referential for relatively positioned arrows */
position: relative;
}
</style>
<!-- outer container -->
<window mode="modal" title="Lightbox prototype" closable="true">
<!-- left arrow -->
<div sclass="content-wrapper">
<cardlayout id="card" width="300px" height="200px">
<!-- content start -->
<div height="100%" width="100%" style="background-color:palegoldenrod;padding:20px">
card 1 content
</div>
<div height="100%" width="100%" style="background-color:steelblue;padding:20px">
card 2 content
</div>
<div height="100%" width="100%" style="background-color:palegoldenrod;padding:20px">
card 3 content
</div>
<div height="100%" width="100%" style="background-color:steelblue;padding:20px">
card 4 content
</div>
<!-- content end -->
</cardlayout>
<a onClick="card.previous()" sclass="lightbox-arrow-left" iconSclass="z-icon-angle-left"/>
<!-- left right arrow -->
<a onClick="card.next()" sclass="lightbox-arrow-right" iconSclass="z-icon-angle-right"/>
<!-- inner container -->
</div>
</window>
</zk>

Now that it’s ready, let’s have a look.

Step 1 result screenshot

Well, everything is there. Our window is floating in the page, with the content cards and the arrows. It looks like this structure matches the requirement list.

Have a look here for the complete page.

Step 2 – Reusability

Now that we are happy with the general look and feel of our control, it’s time to start reusing code fragments, instead of declaring everything inline.

First target the cards themselves. Since we are using a similar layout for each card, we can extract them into unique template files. Those files will contain zul code, which will be inserted at the relevant point in a page.

The original card was declared as:

<div height="100%" width="100%" >
card 1 content
</div>

The only element that needs to change between cards is the text, so we will replace it with a variable. The variable will be resolved, and the value will be inserted in the label when the template is instantiated.

Using templating also lets us create multiple templates for multiple use cases. In this example, we will create two templates, and add a new label to template 1 and template 2 respectively to show which one is invoked.

<div height="200px" width="300px" >
<label value="template 1: " />
<label value="${templateText}"/>
</div>

Once the template has been defined, we only need to call it from the main page. 

<apply templateURI="/templates/template1.zul" templateText="This is the first card" />

We will also pass in the variable used inside the template at this point.

Image title

Have a look here for the completed page and the individual page template.

Step 3 – Integration in a Working Page

The standalone structure is well and good, but if our control is going be used in a real-world scenario, it needs to integrate into a working page. In the end, we want to open our lightbox when the user performs certain actions on the page, like clicking on a link or a button.

Let’s keep in mind that we don’t want the lightbox to always be in the page, but rather to be added to the page when needed.

Let’s start by making a page mockup. To keep it as simple as possible, we will only create four links and have each link open a different card of our lightbox with the ability to navigate between the cards.

<a id="l1">anchor 1</a>
<a id="l2">anchor 2</a>
<a id="l3">anchor 3</a>
<a id="l4">anchor 4</a>

To make sure that the links are easy to click, we will just store them into a vertical layout (or vlayout) component.

We will also store our lightbox zul structure into a separate zul file called lightboxdefinition.zul. This file will not be used as a standalone page but will be instantiated on a need-to basis, and its content will be added to the current page.

The main page will receive a composer. Composers are Java classes which control the page, register events, or trigger behaviors. We will declare the composer on the vlayout containing our anchor links.

<vlayout apply='org.potix.zk_lightbox.LightboxExampleComposer'>

When the user clicks on one of our links, we will listen to that event in our composer and use the Executions.createComponents() method to generate ZK components from the lightboxdefinition.zul file.

For the first listener (on the first anchor link), we use a feature of the SelectorComposer class to target a component using a selector-like syntax.

@Listen("onClick=#l1")

public void handleL1Click(){
openLightbox(0, boxModel);
}

The @Listen annotation will instruct the ZK engine to trigger the handleL1Click() method when an “onClick” event is received on the component with id="l1".

In these listeners, we trigger a private method openLightbox() in which we will pass the index of the card to display, as well as the model for the whole lightbox model. We pass the whole lightbox model because the lightbox can be navigated. When the user clicks on the arrows, we want them to immediately scroll to the relevant card.

private void openLightbox(int index, List<Map<String,String>> model){
  Map<String,Object> arguments = new HashMap<>();
  arguments.put("lightboxModel", model);
  arguments.put("selIndex", index);
  /* createComponents will generate ZK components based on a zul file (or other source)
  * The arguments passed in this call (the model and selected index) will be available
  * to the instantiated components */
  Executions.createComponents("/step3/lightboxdefinition.zul", this.getSelf(), arguments);
}

In this method, we eventually trigger the Executions.createComponents() method. This method will take a page definition — in this case, a zul file — and create the components as described. Those components may be automatically added to a parent if one is declared. However, in this case, we are adding a window with mode='modal', which can be declared anywhere in the page and will display as modal window anyway. Since we pass this.self() as the parent target — the vlayout holding the composer — the resulting content will be added to the page directly.

We also pass an arguments object, which contains variables to be used when instantiating the lightbox from the page definition. These values will be resolved from the ${…} statements in the pageDefinition.zul file.

Have a look here for the completed page, the page composer,  the lightbox definition page, and the lightbox internal composer.

Going Further

That’s good for our current goals, but we could go a lot further in customization and styling. If you are free later, you may want to have a look at the more refined design that we can apply to this composite control.

Here's a screenshot of a fully designed-up lightbox based on our composite control. You will find the full maven project in this git repository.

Step 4 screenshot - fully custom

Looks like we are done here. Here is some recommended reading on lightboxes:

Monitor application stability with Bugsnag to decide if your engineering team should be building new features on your roadmap or fixing bugs to stabilize your application.Try it free.

Topics:
zk framework ,css ,web dev ,tutorial ,lightbox

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}