Package org.jgrapes.webconlet.examples.helloalpine
This conlet serves as a template for writing conlets that use AlpineJS for reactive UI updates driven by a centralised data store. The pattern separates server-side state management (Java model) from client-side reactive rendering (AlpineJS directives) while keeping both sides in sync.
Architecture
The pattern consists of three layers that communicate through well-defined channels:
- Server model -- Java POJO holding the conlet's state, persisted via the key-value store.
- Data store -- TypeScript class extending Map that holds Alpine.reactive() wrapped state objects, keyed by conlet ID. Lives as a global object on window.
- Preview/View templates -- FreeMarker templates that use Alpine directives (x-data, x-init, x-show, @click) to bind the UI to the data store.
Browser-Side Structure
The TypeScript file (*-functions.ts) defines four elements:
1. State Interface
An interface describing the shape of the client-side state per conlet instance. The corresponding server model properties should mirror this structure.
interface ConletState {
worldVisible: boolean;
}
2. Data Store Class
A class extending Map<string, ConletState> that:
- Holds reactive state objects (Alpine.reactive(...)) per conlet instance, keyed by conlet ID.
- Declares onLoad and onUnload as function-typed properties (using definite assignment assertion !:) for use by the framework's data-jgwc-on-load and data-jgwc-on-unload hook attributes. These must be plain functions -- not class methods -- because the framework invokes them without this binding.
- Provides implementation methods (addConletState, removeConletState) that the hook functions delegate to.
3. Global Registration
An instance of the data store is assigned to a window property (declared via declare global). The onLoad and onUnload properties are assigned as standalone functions that close over the store instance and delegate to its methods:
const conletStates = new ConletDataStore(); window.orgJGrapesConletsExampleMyConlet = conletStates;conletStates.onLoad = function(conletEl, _isUpdate) { conletStates.addConletState(conletEl); };
conletStates.onUnload = function(conletEl, isUpdate) { conletStates.removeConletState(conletEl, isUpdate); };
4. Server Notification Handler
A function registered via JGConsole.registerConletFunction() that receives state updates from the server (pushed via NotifyConletView). It modifies the reactive state in the data store, triggering Alpine's automatic DOM updates:
JGConsole.registerConletFunction(
"org.jgrapes.webconlet.examples.myconlet.MyConlet",
"updateState",
function(conletId, newState) {
const state = conletStates.get(conletId);
if (state) {
state.someProperty = newState.someProperty;
}
});
View Template
The view template (*-view.ftl.html) is the reactive UI. It uses three Alpine-related attributes on the root element:
- data-jgwc-on-load="globalObj.onLoad"
- Initialises the conlet's state in the data store when the view is first rendered. The framework calls this before Alpine processes x-init.
- x-data="{ conletId: null, state: null }"
- Declares local Alpine variables. The state reference will point to the reactive object from the global data store.
- x-init="conletId = ...; state = globalObj.get(conletId);"
- Runs after onLoad, obtains the conlet ID from the nearest [data-conlet-id] ancestor, and stores a reference to the global reactive state. Alpine directives in child elements then react to changes in state.
UI elements use Alpine directives against state:
- x-show="state.visible" -- reactive visibility toggle
- x-bind:class="..." -- reactive class binding
- @click="state.value = !state.value; JGConsole.notifyConletModel(conletId, 'toggleValue')" -- local reactive change + server notification
Preview Template
The preview (*-preview.ftl.html) is static content shown in the conlet picker dialog. It carries data-jgwc-on-unload="globalObj.onUnload" so that when the conlet is removed from the page (not just layout-refreshed), the data store cleans up its state entry and optionally shows a notification. The isUpdate parameter distinguishes a layout refresh (true) from actual removal (false).
Server-Side Communication Flow
State changes flow in two directions:
Client to Server (user action)
- User clicks a button with an @click handler.
- The handler mutates the local reactive state (immediate UI update) and calls JGConsole.notifyConletModel(conletId, 'methodName').
- The server's doUpdateConletState method handles the event, updates the Java model, persists it, and responds with NotifyConletView to push the new state back.
- The registered conlet function receives the update and mutates the reactive state (Alpine re-renders affected bindings).
Server to Client (initial render + push)
- On initial view render, the server's doRenderConlet sends a NotifyConletView event with the current model state after sending the rendered HTML.
- The registered conlet function populates the data store's reactive state with the server's values.
- Alpine's bindings reflect the server-provided initial state.
Build Configuration
The jdbld project config (_jdbld/src/jdbld/conlet/HelloAlpine.java) declares a dependency on AlpineJs (provider) and sets up a TypeScript build via NpmExecutor. The TypeScript file is compiled to ESM and output to build/generated/resources/, packaged into the JAR as a conlet resource.
-
ClassesClassDescriptionHello World example using AlpineJS for reactive UI.Model with world’s state.A factory for creating HelloAlpineConlet objects.