Widget Base Classes

Every widget class in ZK must extend from zk.Widget or one of its derived classes. ZK provides several skeletal implementations that serve as base classes for different types of widgets:

Widget Hierarchy Overview

The widget class hierarchy is structured as follows:

zk.Widget (base for all widgets)
├── zul.Widget (standard UI widgets)
├── zul.Label / zul.Button / ... (concrete ZUL widgets)
└── ... (other specialized widget classes)

zk.Widget - The base class for all widgets. Provides:

  • Widget lifecycle methods: bind_(), unbind_(), redraw()
  • Property management and event firing: fire(), $define
  • DOM manipulation: $n() (get root element)
  • Event listener management: domListen_(), domUnlisten_()

zul.Widget - Most common base for standard widgets

  • Extends zk.Widget
  • Used for most custom widgets like buttons, labels, inputs
  • Provides standard widget behavior and styling

For custom client widgets in this guide, use zul.Widget (or zk.Widget for low-level cases), and only extend a concrete zul.* class when you intentionally inherit that component’s behavior.


Basic Widget Class Structure

For this tutorial, we’ll implement a widget named com.foo.SimpleLabel that extends zul.Widget. Here’s the complete structure:

zk.$package('com.foo');

com.foo.SimpleLabel = zk.$extends(zul.Widget, {
    // 1. Property definitions
    $define: {
        value: null
    },

    // 2. Render HTML
    redraw: function(out) {
        out.push('<span', this.domAttrs_(), '>',
                 this.getValue() || '',
                 '</span>');
    },

    // 3. Lifecycle: Attach to DOM
    bind_: function() {
        this.$supers('com.foo.SimpleLabel', 'bind_', arguments);
        // Initialize event listeners here
    },

    // 4. Lifecycle: Detach from DOM
    unbind_: function() {
        // Clean up event listeners here
        this.$supers('com.foo.SimpleLabel', 'unbind_', arguments);
    }
});

Widget Implementation Components

A complete widget implementation typically includes these components:

1. Package Declaration

zk.$package('com.foo');
  • Organizes your widgets into a namespace
  • Prevents global namespace pollution
  • Convention: matches server-side Java package

2. Class Definition

com.foo.SimpleLabel = zk.$extends(zul.Widget, {
    // implementation
});
  • Use zk.$extends(baseClass, members, staticMembers)
  • Choose appropriate base class:
    • zul.Widget for most widgets
    • zk.Widget for low-level custom widgets
    • A concrete zul.* widget class if you want to reuse its built-in behavior

3. Property Definitions

$define: {
    value: null,
    enabled: true,
    visible: null
}

Defines widget properties with automatic getters/setters. The $define keyword is processed by ZK to generate:

  • getValue() / setValue(value)
  • isEnabled() / setEnabled(boolean)
  • isVisible() / setVisible(boolean)

See Implementing a Widget Property for detailed property patterns.

4. The redraw() Method

redraw: function(out) {
    out.push('<span', this.domAttrs_(), '>',
             zUtl.encodeXML(this.getValue() || ''),
             '</span>');
}

Generates the HTML for the widget. Called by ZK framework to render the widget in the browser.

redraw() is the rendering entry point. If your widget supports molds, redraw() typically delegates rendering to the active mold (selected by getMold()), while the mold method contains the concrete HTML structure. If you only need one rendering style, implementing redraw() directly is sufficient.

Key methods:

  • out.push() - Append HTML strings to output
  • this.domAttrs_() - Generate standard DOM attributes (id, class, style, etc.)
  • zUtl.encodeXML() - Safely encode values for HTML content

See The Redraw Method for more details, and Implementing Molds for mold-based rendering.

5. Lifecycle Methods

bind_() - Called when widget attaches to DOM

bind_: function() {
    this.$supers('com.foo.SimpleLabel', 'bind_', arguments);
    // Initialize event listeners
    this.domListen_(this.$n(), 'onClick', '_doClick');
}

Called after redraw() completes and widget is inserted into the DOM. Use this to:

  • Register event listeners with domListen_()
  • Initialize widget state
  • Set up timers or resource bindings

unbind_() - Called when widget detaches from DOM

unbind_: function() {
    this.domUnlisten_(this.$n(), 'onClick', '_doClick');
    this.$supers('com.foo.SimpleLabel', 'unbind_', arguments);
}

Called when widget is removed from the DOM. Use this to:

  • Clean up event listeners with domUnlisten_()
  • Release resources
  • Prevent memory leaks

See How we Implement the Event for event handling details.

6. Event Handlers

For widget events, you can use either custom handlers or override ZK’s built-in protected hooks.

Custom handler registered in bind_()

_doClick: function(evt) {
    // Handle the click event
    this.fire('onSimpleClick', {data: 'clicked'});
}

Private methods (starting with _) handle DOM events you register with domListen_() in bind_().

Override built-in protected hook (example: doClick_)

doClick_: function (evt) {
    // Custom behavior before/after default processing
    this.fire('onSimpleClick', {data: 'clicked'});
    this.$supers('doClick_', arguments); // keep default click handling
}

Common built-in protected hooks in zk.Widget include:

  • doClick_()
  • doDoubleClick_()
  • doRightClick_()
  • doMouseDown_(), doMouseUp_(), doMouseOver_(), doMouseOut_()
  • doKeyDown_(), doKeyUp_(), doKeyPress_()

Key points:

  • Are called automatically when their registered event occurs
  • Can update widget state
  • Can fire custom events using this.fire()
  • When overriding doXxx_(), usually call this.$supers('doXxx_', arguments) to preserve default behavior

Complete Widget Template

Here’s a template you can use as a starting point:

zk.$package('com.example');

com.example.MyWidget = zk.$extends(zul.Widget, {
    // Properties
    $define: {
        text: null,
        enabled: true,
        style: null
    },

    // Generate HTML
    redraw: function(out) {
        out.push('<div', this.domAttrs_(), ' class="my-widget">',
                 zUtl.encodeXML(this.getText() || ''),
                 '</div>');
    },

    // Setup when attached to DOM
    bind_: function() {
        this.$supers('com.example.MyWidget', 'bind_', arguments);
        // Register listeners
        this.domListen_(this.$n(), 'onClick', '_doClick');
        this.domListen_(this.$n(), 'onMouseover', '_doHover');
    },

    // Event handlers
    _doClick: function(evt) {
        this.fire('onMyClick', {});
    },

    _doHover: function(evt) {
        // Handle hover
    },

    // Cleanup when detached from DOM
    unbind_: function() {
        this.domUnlisten_(this.$n(), 'onClick', '_doClick');
        this.domUnlisten_(this.$n(), 'onMouseover', '_doHover');
        this.$supers('com.example.MyWidget', 'unbind_', arguments);
    }
});

Key Implementation Details

Using this.desktop to Check Widget State

Always check if the widget is attached before updating the DOM:

setValue: function(value) {
    this._value = value;
    if (this.desktop) {  // Only update DOM if attached
        this.$n().textContent = value;
    }
}
  • this.desktop is null if widget is not attached
  • this.desktop is the zk.Desktop instance if widget is attached
  • Used to prevent DOM updates before rendering

Using this.$n() to Access DOM Element

this.$n()  // Returns the root DOM element of the widget

From a rendered widget like <span id="z_xyz_3">content</span>, $n() returns the <span> element.

Calling Parent Methods with $supers()

this.$supers('com.foo.SimpleLabel', 'methodName', arguments);

Always call parent class methods for lifecycle functions to maintain proper inheritance chain.


Widget Class Reference

The widget implementation framework is defined in zk.ts, providing:

  • zk.$extends() - Class definition
  • zk.Widget - Base widget class with lifecycle methods
  • $define - Property definition processor
  • Lifecycle: redraw(), bind_(), unbind_()