BOUI Framework

The JavaScript layer that brings dynamic behavior and interactivity to server-rendered HTML components generated by the Uxmal PHP framework.
What is BOUI?
BOUI acts as a bridge, connecting the static HTML structure with client-side functionality like handling clicks, fetching data, updating the UI, and managing component states.
javascript
Core Concept: HTML Attributes Drive Behavior
The central idea is declarative initialization and interaction using standard HTML data-* attributes.

Here's how it works:

  1. Backend (PHP/Uxmal): When generating HTML components (like modals, forms, buttons), Uxmal adds specific data-uxmal-* attributes to the elements. These attributes tell the frontend what the element is and how it might behave.
      data-uxmal-modal="myModalName": Identifies this element as a BOUI modal component named "myModalName". data-uxmal-id="uniqueId123": Gives the component instance a unique ID. data-uxmal-action="fetch": Specifies that this element should perform a 'fetch' action. data-uxmal-action-on="click": Specifies the action should trigger on a 'click' event. data-uxmal-route="api.users.list": Provides the named route for an API call.
  2. Frontend (BOUI JavaScript): When the page loads, BOUI's JavaScript scans the HTML document for these data-uxmal-* attributes.
  3. Initialization: Based on the attributes found, BOUI automatically:
    • Instantiates Component Classes: If it finds data-uxmal-modal, it creates a new JavaScript UIModal object linked to that HTML element.
    • Attaches Actions: If it finds data-uxmal-action and data-uxmal-action-on, it automatically adds the correct event listener (e.g., a click listener) that will execute the specified action's JavaScript code (e.g., code to perform an API fetch).

This approach minimizes the need for custom JavaScript for common component initialization and interactions.

How to Integrate BOUI
Integrating BOUI into your Laravel project is straightforward.

Since BOUI is included as a Composer package (uxmaltech/backoffice-ui), its JavaScript assets reside within the vendor directory.

You need to import the main BOUI entry point into your project's primary JavaScript file (usually resources/js/app.js) before it's compiled by Vite or Mix.

Integration Example
After adding the import, ensure your build process (Vite or Mix) compiles resources/js/app.js. Once compiled, the BOUI framework will automatically initialize itself when the page loads.
javascript
How it Starts: Initialization Flow
Understanding the BOUI initialization process helps you work with it more effectively.

When the web page finishes loading (DOMContentLoaded):

  1. boui/index.js runs: This is the main entry point (imported via your app.js).
  2. Loads Config: Fetches necessary info from the backend (like API routes).
  3. Finds Components: Scans the HTML for data-uxmal-* component attributes.
  4. Creates JS Objects: For each component found, it creates the corresponding JavaScript object (e.g., UIModal, UIForm).
  5. Stores Objects: Registers these new JS objects in a central place called bouiState.
  6. Finds Actions: Scans the HTML for data-uxmal-action attributes.
  7. Attaches Listeners: Sets up event listeners (click, change, etc.) for elements with actions.
  8. Ready: The framework signals it's ready (loaded.boui.app event).
Building Blocks: Components (bouiElement)
Understanding the component system in BOUI.
  • Most interactive elements managed by BOUI have a corresponding JavaScript class (e.g., UIModal, UIButton, UIFormInput).
  • These classes usually extend a base class called bouiElement.
  • An instance of a component class holds a reference to its HTML element (.el) and provides methods specific to that component (e.g., modal.show(), button.loader(true)).
  • Components are automatically instantiated during the initialization phase based on the HTML attributes.
Keeping Track: State Management (bouiState)
How BOUI manages state across your application.
  • Central Registry: bouiState is the heart of the framework's frontend state. Think of it as a big JavaScript object (using Maps internally) that knows about all the active BOUI component instances on the page.
  • Why? Instead of constantly searching the DOM (document.querySelector), you can ask bouiState for a component instance if you know its name or ID.
  • Accessing Components: You primarily interact with bouiState through the global boui object:
javascript
  • Event Bus: bouiState also manages an internal event system (dispatch and on). Components and other parts of the system can communicate without being directly coupled.
Making Things Happen: Actions
Actions provide interactivity without writing repetitive JavaScript.
  • An element like: <button data-uxmal-action-on="click" data-uxmal-action="dispatch" data-uxmal-event-name="user:reload">Reload</button> tells BOUI:
    • "When this button is clicked (action-on="click")..."
    • "...perform the dispatch action (action="dispatch")..."
    • "...dispatching an event named user:reload (event-name="user:reload")."
  • BOUI has built-in actions for common tasks like dispatching events (dispatch), fetching data from the API (fetch), submitting forms (submit-form), and running arbitrary JavaScript (javascript).
Talking to the Server: API Interaction
BOUI provides a streamlined way to make API calls to the backend.
  • BOUI provides a streamlined way to make API calls to the backend using named routes defined by the server.
  • The bouiState.fetch() method (often accessed via boui.cbq() - Command Bus Query) handles this.
javascript
  • It automatically uses the correct HTTP method, URL, parameters, and adds necessary headers (CSRF, Auth token) based on the named route configuration fetched during initialization.
Using BOUI in Your Code: The Global boui Object
The main entry point for interacting with the framework.
  • BOUI exports a global object (the instance created in boui/index.js). You typically import this into your custom JS files.
  • This is your main entry point for interacting with the framework:
    • Get a component instance: boui.get('componentNameOrId').
    • Dispatch an event: boui.dispatch('eventName', { data }).
    • Listen for an event: boui.on('eventName', callback).
    • Make an API call: boui.cbq('routeName', params, payload).
    • Control logging verbosity: boui.logLevel('DEBUG').
  • In development mode, boui/index.js assigns this instance to window.boui for easy console access.
Interaction Examples
Practical examples of working with BOUI components.
Controlling a Modal
This example shows how to interact with a modal that has data-uxmal-modal="userEditModal" and data-uxmal-id="modal-user-edit-123".
javascript
Toggling an Offcanvas Menu
This example shows how to interact with an offcanvas element that has data-uxmal-offcanvas="mainMenu" and data-uxmal-id="offcanvas-main-menu".
javascript

Key takeaways from examples:

  • Import the boui object.
  • It's often safest to interact with BOUI after the loaded.boui.app event.
  • Use boui.get('component-id') to retrieve the specific JavaScript instance of the component.
  • Call methods directly on the retrieved instance (e.g., modalInstance.show(), offcanvasInstance.toggle()).
Playing Nicely: Livewire Integration
BOUI works seamlessly with Livewire.

BOUI listens for Livewire DOM updates and automatically initializes any new BOUI components or actions that appear within the updated HTML fragments rendered by Livewire.

javascript

When you dispatch Livewire events using Livewire.dispatch('myEvent') you can wait for the new components to be rendered and initialized using boui.waitFor.

javascript

This approach ensures that your BOUI components are always properly initialized after Livewire updates, and you can react to Livewire events and new UI fragments in a reliable way.

Real-Time: WebSocket Integration (Ably)
BOUI supports real-time features via Ably WebSocket.
javascript

After calling boui.connectWebsocket(), the boui.websocket property gives you access to the Ably instance for real-time messaging, pub/sub, and presence features.

Make sure your environment is configured with VITE_UXMAL_WEBSOCKET_TOKEN containing a valid Ably token. Without this, the connection will fail.

BOUI API Reference

Key methods and properties available on the global boui object.

Common alternative names: Frontend Framework, JavaScript Layer, Client-side Library, UI Framework

Core Features: Component Initialization, Event Handling, API Communication, State Management, DOM Manipulation


Principles

Declarative

BOUI uses HTML attributes to define behavior, making your markup self-documenting. The HTML structure tells the story of what happens without requiring separate JavaScript files.

Automatic

Components initialize themselves based on their attributes. BOUI scans the DOM and connects all the pieces with minimal developer effort.

Coordinated

The central state management system allows components to find and communicate with each other through a standardized event system.

Server-Connected

The framework streamlines API communication through a named route system that handles parameters, authentication, and response processing.


Architecture

  1. HTML Attributes
  2. Uxmal PHP renders HTML with data-uxmal-* attributes that define components and behaviors

  3. BOUI Initialization
  4. BOUI scans the DOM for tagged elements and creates JavaScript component instances

  5. Component Registry
  6. All component instances are stored in the bouiState central registry for easy access

  7. Event System
  8. Components communicate through a publish/subscribe event system

  9. API Layer
  10. Named routes simplify server communication with automatic parameter handling


Usage

BOUI is designed to work hand-in-hand with the Uxmal PHP framework. When Uxmal generates HTML components on the server, it adds data-uxmal-* attributes that BOUI recognizes. This allows for automatic initialization of interactive components like modals, forms, and dropdowns without writing custom JavaScript.

While BOUI handles much of the initialization automatically, you can also interact with it programmatically when you need custom behavior beyond what the attribute-based system provides.

Common Use Cases