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.Core Concept: HTML Attributes Drive Behavior
The central idea is declarative initialization and interaction using standard HTMLdata-*
attributes.Here's how it works:
- 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. - Frontend (BOUI JavaScript): When the page loads, BOUI's JavaScript scans the HTML document for these
data-uxmal-*
attributes. - Initialization: Based on the attributes found, BOUI automatically:
- Instantiates Component Classes: If it finds
data-uxmal-modal
, it creates a new JavaScriptUIModal
object linked to that HTML element. - Attaches Actions: If it finds
data-uxmal-action
anddata-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).
- Instantiates Component Classes: If it finds
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) compilesresources/js/app.js
. Once compiled, the BOUI framework will automatically initialize itself when the page loads.How it Starts: Initialization Flow
Understanding the BOUI initialization process helps you work with it more effectively.When the web page finishes loading (DOMContentLoaded):
- boui/index.js runs: This is the main entry point (imported via your
app.js
). - Loads Config: Fetches necessary info from the backend (like API routes).
- Finds Components: Scans the HTML for
data-uxmal-*
component attributes. - Creates JS Objects: For each component found, it creates the corresponding JavaScript object (e.g.,
UIModal
,UIForm
). - Stores Objects: Registers these new JS objects in a central place called
bouiState
. - Finds Actions: Scans the HTML for
data-uxmal-action
attributes. - Attaches Listeners: Sets up event listeners (click, change, etc.) for elements with actions.
- 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 askbouiState
for a component instance if you know its name or ID. - Accessing Components: You primarily interact with
bouiState
through the globalboui
object:
- Event Bus:
bouiState
also manages an internal event system (dispatch
andon
). 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"
)."
- "When this button is clicked (
- 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 viaboui.cbq()
- Command Bus Query) handles this.
- 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')
.
- Get a component instance:
- In development mode,
boui/index.js
assigns this instance towindow.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".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".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.
When you dispatch Livewire events using Livewire.dispatch('myEvent')
you can wait for the new components to be rendered and initialized using boui.waitFor
.
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.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
- HTML Attributes
- BOUI Initialization
- Component Registry
- Event System
- API Layer
Uxmal PHP renders HTML with data-uxmal-* attributes that define components and behaviors
BOUI scans the DOM for tagged elements and creates JavaScript component instances
All component instances are stored in the bouiState central registry for easy access
Components communicate through a publish/subscribe event system
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.