Building Stateless UI"

From Documentation
Line 83: Line 83:
  
 
= Event Wiring =
 
= Event Wiring =
To wire an action handler method for an event, you need to apply the annotation <code>@Action</code> on a method:
+
To wire an action handler method for an event, you need to call <code>withAction(ActionHandler action)</code> with a '''public method''' reference:
* The method should be public.
 
* The parameter of <code>@Action</code> should be one of  [https://www.zkoss.org/javadoc/zk/org/zkoss/zk/ui/event/Events.html event types].
 
* Wire the action handler with the target component by <code>withAction(ActionHandler action)</code>.
 
  
<source lang='java' highlight='2, 9'>
+
<source lang='java' highlight='3'>
    // ActionHandler method
+
IButton.of("add item +")
    @Action(type = Events.ON_CLICK) // Wiring event
+
    .withSclass("add-items")
    public void addItem() {
+
    .withAction(ActionType.onClick(this::addItem))
        ...
 
    }
 
 
 
    public IComponent demo() {
 
        return IButton.of("add items").withSclass("add-items")
 
                .withAction(this::addItem);
 
    }
 
 
</source>
 
</source>
 +
* Line 3: it means register <code>addItem()</code> as an action handler for <code>onClick</code> event on <code>IButton</code>
  
Here, we use '''action handler''' to differentiate an event listener of a classic component.
+
In stateless components, we use the term 'action handler', which distinctly separates it from the event listener associated with classic components.
  
 
= Obtain Widget State =
 
= Obtain Widget State =

Revision as of 05:41, 24 November 2023


Building Stateless UI


Setting up

To set up stateless components in a ZK 10 application, you need to include the stateless components module and define a Dispatcher Richlet Filter in your WEB-INF/web.xml file.

Including Required Jar

dependencies {
    implementation "org.zkoss.zk:stateless:${zkVersion}"
    ...
}

Dispatcher Richlet Filter

<filter>
    <filter-name>DispatcherRichletFilter</filter-name>
    <filter-class>org.zkoss.stateless.ui.http.DispatcherRichletFilter</filter-class>
    <init-param>
        <param-name>basePackages</param-name>
        <param-value><!-- your base packages --></param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>DispatcherRichletFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>


Example Application

We will use the simple shopping cart application as an example to introduce basic features of stateless components:

Shoppingcart.png

Building UI with Richlet

Building user interfaces (UI) with stateless components requires creating a StatelessRichlet and mapping a URL to that richlet.

URL Mapping

We use @RichletMapping to compose a URL. When users visit that URL, ZK will invoke the corresponding method.

For example below, the index() URL will be <protocal>:// <host name: port> /shoppingCart.

    @RichletMapping("/shoppingCart")
    public class DemoRichlet implements StatelessRichlet {
        @RichletMapping("")
        public List<IComponent> index() {
            //return ...
        }
    }


Class-Level Mapping

At this level, @RichletMapping defines the base path for all methods in a StatelessRichlet. For example, assigning @RichletMapping("/shoppingCart") to the DemoRichlet class sets a foundational path for all UI components it manages.


Method-Level Mapping

Within the StatelessRichlet, each method can specify further URL mapping. By applying @RichletMapping("") to a method, the specified path appends to the class-level path.

Composing the UI with Stateless Components

Before ZK 10, ZK components are stateful, meaning that the server holds the state. Starting from ZK 10, we provide a set of stateless components as Immutable objects. Immutable objects are constructed once and can not be changed after they are constructed. After Immutable objects are rendered, they will be destroyed. Since the component state will not be saved on the server, they consume less memory.

UI Composing

With ZK 10 stateless components, you will no longer write a zul file. You will be composing your view using the stateless components and their APIs in Java.

  • ZK component having a prefix letter "I" represents an immutable component.
  • We offer of() API for commonly used properties.
  • withSclass() means the setter of sclass.
    // ZK 10
    IButton.of("add items")
           .withSclass("add-items");
    // ZK 9
    // equivalent idea as above.
    Button button = new Button("add items");
    button.setSclass("add-items");

Event Wiring

To wire an action handler method for an event, you need to call withAction(ActionHandler action) with a public method reference:

IButton.of("add item +")
    .withSclass("add-items")
    .withAction(ActionType.onClick(this::addItem))
  • Line 3: it means register addItem() as an action handler for onClick event on IButton

In stateless components, we use the term 'action handler', which distinctly separates it from the event listener associated with classic components.

Obtain Widget State

Since a server no longer holds a component's state (it's on the client side), we provide @ActionVariable to access a UI component's state sent from the client. When ZK invokes an action handler for an event, it will pass the corresponding parameters you specified.

  • @ActionVariable(targetId = ActionTarget.SELF, field = "id") retrieves the value from the field of a component with the targetId on the client.
  • ActionTarget.SELF means it targets the component associated with the event itself.
    @Action(type = Events.ON_CLICK)
    public void addItem(@ActionVariable(targetId = ActionTarget.SELF, field = "id") String orderId) {
    }

Update Widget State

ZK provides various APIs on UiAgent to update a widget's state. You need to call its method in an action handler method. The widget states will be updated to the client after executing the method.

The following code adds the specified child component as the last child to the component found by Locator.

@Action(type = Events.ON_CLICK)
public void addItem(@ActionVariable(targetId = ActionTarget.SELF, field = "id") String uuid) {
    UiAgent.getCurrent().appendChild(Locator.ofId(SHOPPING_CART_ROWS),
            renderShoppingCartOneItem(parseOrderId(uuid)));
}

Example Application

You can download the shopping cart demo project.




Last Update : 2023/11/24

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