Server-side selector provides flexible DOM manipulation

Web application framework and Java middleware

Server-side selector provides flexible DOM manipulation

TheServerSide.com

jQuery made DOM manipulation on the client side much more concise thanks to its CSS Selector mechanism, among other things. Now, the CSS selector technique has been extended to server side technology in ZK 6 under the guise of Java annotations.

 

The Convention

ZK is a component based, event driven Ajax framework. Client side code and HTML are generated on the server side in response to HTTP requests. The conventional technique for binding data between the presentation and data objects, as well as mapping actions to methods, is having a binding mechanism based on name string matching, such as using EL with Managed Beans in JSF or MVVM in ZK.

For instance, in a JSF file we might have a contact info form like the following:

<h:form binding=”#{contactController.form}”>
        <h:panelGrid
                columns="2">
            <h:outputLabel value="First Name"></h:outputLabel>
            <h:inputText 
                value="#{contactController.contact.firstName}"></h:inputText>
                        
...
                       
         </h:panelGrid>
            <h:panelGroup>
                <h:commandButton action=”#{contactController.save}” value=”Submit”>
                </h:commandButton>
            </h:panelGroup>
</h:form>

With the following corresponding Java classes:

public class Contact {
           private String firstName;
            …
           public void setFirstName()
{...}
}
public class ContactController {
            private UIForm form;
            ...
            public Contact getContact()
{...}
            ...
public String save() {...}
}

While such a binding mechanism is adequate for fixed forms or templates, programming a more dynamic and responsive UI would be easier with a more flexible control on the DOM elements.

 

An Alternative

Suppose we are asked to implement a Contact Info form as shown here:

 

Let’s examine how having server-side selector at our disposal would help us implement these two requirements:

  1. When user selects either “US” or “Canada” in the combo box labeled “Country”, the bottom row should reflect a field appropriate to the selection. More specifically, a label “State” with a combo box containing a list of US states should appear whenever “US” is chosen; whereas a label “Province” with a combo box listing some Canadian provinces should present itself whenever “Canada” is selected.
  2. All fields are cleared when user clicks the “Clear” button.

 

We’ll begin with the mark-up in ZK:

...
<grid  apply=”demo.zkoss.selector.ContactCompoer”>
            ...
            <rows>
                      <row>
                             <label value="Country"/>
                                   <combobox sclass="country-select" width="90%">
                                               <comboitem label="Canada"/>
<comboitem label="US"/>
                                   </combobox>
                       </row>
                       ...
                       <row id="statesRow">
                                   <label value="State"/>
                                   <combobox width="90%">
                                               ...
                                               <comboitem label="MI"/>
                                   </combobox>
                       </row>
                       <row visible="false">
                                   <label value="Province" />
                                   <combobox width="90%">
                                               <comboitem label="BC"/>
                                               ...
                                   </combobox>
                       </row>
            </rows>
</grid>

From examining the mark-up above, note that the component tags themselves are not bound with any data object or controller methods, only the grid component is “applied” with a controller named ContactComposer. The class is shown below:

public class ContactComposer extends SelectorComposer {
                                   
@Wire("#statesRow")
            Row states;
                       @Wire("#statesRow + row")
            Row provinces;
            @Listen("onSelect = combobox
.country-select")
            public void
onCountrySelected(Event event){
                       Combobox countrybox = (Combobox)event.getTarget();
                       if(countrybox.getSelectedIndex() != 0){
                                   states.setVisible(true);
                                   provinces.setVisible(false);
                       } else {
                                   provinces.setVisible(true);
                                   states.setVisible(false);
                       }
            }
                       @Wire("textbox, combobox")
            List<InputElement>
inputs;
           
            @Listen("onClick =
button[label='Clear']")
            public void clearForm(){
                       for(InputElement i:inputs) i.setText("");
            }
 }

 

The Mechanism

Under the ZK framework, the DOM elements have Java components on the server side that act as representatives to maintain DOM states and controllers to act on events. The annotation “@Wire” binds a component declaration in the Java controller to a markup tag (or tags) that match the selector pattern given as the annotation’s parameter.

For instance, the snippet establishes a reference from the Row instance “states” to <row id=”statesRow”/>.

            @Wire(“#statesRow”)
            Row states;

Similarly, following the CSS Selector patterns, @Wire(“#statesRow + row”) selects the next sibling row to the row with ID “statesRow”. With the “@Wire” annotation, referencing a markup tag is as flexible as the CSS Selector could take us.

The same mechanism is employed to listen to action events. For example, the annotation @Listen(“onSelect = combobox.country-select”) adds the annotated method onCountrySelected as an event listener to the combo box component that has the specified styling class name “country-select” and listens to its onSelect event.

 

Requirements Concisely Met

Now let's dive into how the two requirements are satisfied with the Selector mechanism.

To implement the first requirement, we’d need to have access to the two rows declared, one contains a selection of states, the other of provinces, such that we can turn their visibility on and off according the selection that users make in the “Country” combo box. Courtesy of the Server-side Selectors, we easily obtained references to both.    

@Wire(“#statesRow”)
            Row states;
            @Wire(“#statesRow + row”)
            Row provinces;

We’ve also registered an event listener for the combo box. So it’s straightforward enough to make the appropriate row visible on the fly whenever a selection is made.

            @Listen("onSelect = combobox.country-select")
            public void onCountrySelected(Event event){
                       Combobox countrybox = (Combobox)event.getTarget();
                       if(countrybox.getSelectedIndex() != 0){
                                   states.setVisible(true);
                                   provinces.setVisible(false);
                       } else {                                    provinces.setVisible(true);                                    states.setVisible(false);                       }             }

For the second requirement, having the Server-side Selector really makes the implementation concise. We first wire all the input elements, text boxes and combo boxes alike, and declare a list of InputElement to reference them:

@Wire("textbox, combobox")
List<InputElement> inputs;

We then add a listener to the “Clear” button by giving the annotation a Selector pattern that matches the button. The clearForm method couldn’t be simpler as we just iterate through the list of input elements and assign an empty string to nullify any inputs already made.

            @Listen("onClick =
button[label='Clear']")
            public void clearForm(){
                       for(InputElement i:inputs) i.setText("");
            }

 

The Gist

For our hypothetical requirements, arming a component based, event driven framework such as ZK with the ability to access component(s) using simple, conventional CSS Selector patterns allows us to achieve flexible UI manipulation that echoes client side programming with jQuery. With Server-side Selectors, the same responsive UI experience can be brought forth just as easily on the server side. Furthermore, as demonstrated in the sample code, the UI mark-up is left unchanged throughout our programming, thus achieving a greater separation of concerns in a different flavor.

For further readings, here are an introduction and a reference document.

22 Mar 2012

Related Content

Related Resources

Disclaimer: Our Tips Exchange is a forum for you to share technical advice and expertise with your peers and to learn from other enterprise IT professionals. TechTarget provides the infrastructure to facilitate this sharing of information. However, we cannot guarantee the accuracy or validity of the material submitted. You agree that your use of the Ask The Expert services and your reliance on any questions, answers, information or other materials received through this Web site is at your own risk.