CBWire (ColdFusion Livewire) follows patterns similar to Laravel Livewire. Here's a breakdown of when to use each:
Data Properties
Data properties are the reactive state of your component. They are:
- Persisted between requests (stored in component state)
- Two-way bindable via
wire:model - Automatically available in your view
- Included in the component's hydration/dehydration lifecycle
// MyComponent.cfc
component extends="cbwire.models.Component" {
data = {
"firstName": "",
"lastName": "",
"items": [],
"searchTerm": ""
};
function save() {
var user = userService.create(
firstName = data.firstName,
lastName = data.lastName
);
}
}
<!-- MyComponent.cfm -->
<input type="text" wire:model="firstName" placeholder="First Name">
<input type="text" wire:model="lastName" placeholder="Last Name">
<button wire:click="save">Save</button>
Use data properties when you need to:
- Bind form inputs with
wire:model - Store user input or selections
- Track state that changes due to user interaction
- Persist values across multiple requests in the component lifecycle
Computed Properties
Computed properties are derived values calculated on the fly. They are:
- Not stored in component state
- Cached per request (the function only runs once per request even if called multiple times)
- Read-only (cannot be bound with
wire:model) - Ideal for expensive operations or derived data
- Registered via the
computedannotation after the function name - Cache can be bypassed by passing
falseas the first argument
// ProductListComponent.cfc
component extends="cbwire.models.Component" {
data = {
"searchTerm": "",
"categoryId": 0,
"sortBy": "name"
};
// computed annotation registers this as a computed property
function getFilteredProducts() computed {
return productService.search(
term = data.searchTerm,
categoryId = data.categoryId,
sortBy = data.sortBy
);
}
function getTotalCount() computed {
// Reuses cached getFilteredProducts() - no second DB call!
return getFilteredProducts().len();
}
function getHasResults() computed {
return getTotalCount() > 0;
}
function getFreshCount() computed {
// Pass false to bypass cache and force a fresh DB call
return getFilteredProducts( false ).len();
}
}
<!-- ProductListComponent.cfm -->
<input type="text" wire:model.live.debounce.300ms="searchTerm" placeholder="Search...">
<cfif getHasResults()>
<p>Showing #getTotalCount()# results</p>
<cfloop array="#getFilteredProducts()#" item="product">
<div class="product-card">
<h3>#product.getName()#</h3>
<p>#product.getPrice()#</p>
</div>
</cfloop>
<cfelse>
<p>No products found.</p>
</cfif>
<!-- Force a fresh result outside of the cache -->
<p>Live count: #getFilteredProducts( false ).len()#</p>
Use computed properties when you need to:
- Derive data from existing data properties
- Run database queries based on current state
- Avoid storing redundant data (e.g.
getFullName()derived fromfirstName+lastName) - Call the same expensive operation multiple times in a template without re-running it
- Need a fresh result mid-render by passing
falseto bypass the cache
Side-by-Side Example
component extends="cbwire.models.Component" {
data = {
"firstName": "John", // DATA: stored in state, bindable
"lastName": "Smith", // DATA: stored in state, bindable
"items": [] // DATA: stored in state, bindable
};
// COMPUTED: derived, cached, not stored in state
function getFullName() computed {
return data.firstName & " " & data.lastName;
}
// COMPUTED: derived from a data property
function getItemCount() computed {
return data.items.len();
}
// COMPUTED: expensive operation, cached per request
function getRecentOrders() computed {
return orderService.getRecentOrders( userId = auth().getUserId() );
}
}
<!-- Two-way binding works on DATA properties -->
<input wire:model="firstName">
<input wire:model="lastName">
<!-- Computed properties are read-only, available in the template -->
<p>Hello, #getFullName()#!</p>
<p>You have #getItemCount()# items</p>
<p>Recent orders: #getRecentOrders().len()#</p>
<!-- Bypass cache to get a fresh result -->
<p>Fresh order count: #getRecentOrders( false ).len()#</p>
Quick Decision Guide
| Scenario | Use |
|---|---|
| Binding a form input | Data |
| Storing user selections/toggles | Data |
| Deriving a value from other data | Computed |
| Running a DB query based on filters | Computed |
| Value changes via user interaction | Data |
| Value that would be redundant to store | Computed |
Need wire:model two-way binding | Data |
| Expensive logic called multiple times in view | Computed (cached) |
| Need a guaranteed fresh result mid-render | Computed with false
|
The biggest practical benefit of computed properties in CBWire is the
per-request caching - if your template calls
getFilteredProducts() five times, the database query only
runs once. When you genuinely need a fresh result, simply pass
false to bypass the cache. This makes computed properties
ideal for wrapping service calls driven by your data property filters,
without worrying about redundant queries.