Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
CBWIRE is a ColdBox module that makes building modern, reactive CFML apps a breeze without the need for JavaScript frameworks such as Vue or React, and without the hassle of creating unnecessary APIs.
<!--- ./layouts/Main.cfm --->
<cfoutput>
<!doctype html>
<html>
<head>
#wireStyles()#
</head>
<body>
#wire( "Counter" )#
#wireScripts()#
</body>
</html>
</cfoutput>
// ./wires/Counter.cfc
component extends="cbwire.models.Component" {
// Data Properties
data = {
"counter": 0
};
// Actions
function increment(){
data.counter += 1;
}
}
<!--- ./views/wires/counter.cfm --->
<cfoutput>
<div>
<div>Count: #args.counter#</div>
<button wire:click="increment">
+
</button>
</div>
</cfoutput>You can listen for livewire:load and place any JavaScript there.
<div>
<!--- Your component's template --->
<script>
document.addEventListener('livewire:load', function () {
// Your JS here.
})
</script>
</div>Beautifully integrate your client-side JavaScript and CBWIRE using AlpineJS.
<head>
<script src="//unpkg.com/alpinejs" defer></script>
<!-- The "defer" attribute is important to ensure Alpine waits for CBWIRE to load first. -->
</head><div>
<div x-data="{ open: false }">
<button @click="open = true">Show More...</button>
<ul x-show="open" @click.away="open = false">
<li><button wire:click="archive">Archive</button></li>
<li><button wire:click="delete">Delete</button></li>
</ul>
</div>
</div>// wires/Counter.cfc
component extends="cbwire.models.Component" {
data = {
"counter": 0
};
function increment() {
data.counter += 1;
}
}<!--- views/wires/counter.cfm --->
<cfoutput>
<div x-data="{ counter: #entangle( 'counter' )# }">
<div>CBWIRE value: #args.counter#</div>
<div>AlpineJS value: <span x-html="counter"></span></div>
<button
wire:click="increment"
type="button">Increment with CBWIRE</button>
<button
@click="counter += 1"
type="button">Increment with AlpineJS</button>
</div>
</cfoutput><div x-data="{ counter: #entangle( 'counter' )# }"><button wire:click="increment" type="button">Increment with CBWIRE</button>
<button @click="counter += 1" type="button">Increment with AlpineJS</button><div x-data="{ counter: #entangle( 'counter' )#.defer }">Toggle UI elements when the user is offline or online. Groovy!
<div wire:offline><!-- Oh no, you're offline --></div><div wire:offline.class="online" class="online"></div><div wire:offline.class.remove="im-online" class="im-online"></div>Alter CBWIRE's behavior with these nifty configuration options.
Similar to ColdBox, you can relocate users using relocate.
Poll for state changes based on a specified interval without page refreshes. Hot dog!
Prefetch state changes when the user mouses over an HTML element. Fantastico!
Track changes to Data Properties and display changes in your UI instantly.
Just as with ColdBox, you can inject your dependencies using WireBox.
Call an action immediately once your Wire is first rendered.


component extends="cbwire.models.Component"{
// Action
function clickButton() {
// Relocate to Google
relocate( url="https://www.google.com" );
// Relocate using URI
relocate( uri="/some/relative/path" );
// Relocate using event and set variables in Flash Ram
relocate( event="example.index", persistStruct={
success: true
} );
}
}<div wire:poll></div><div>
<button wire:click.prefetch="togglePreview">Show Preview</button>
<cfif args.showPreview>
<!--- Preview goes here --->
</cfif>
</div>component extends="cbwire.models.Component"{
data = {
"showPreview": false
};
function togglePreview(){
data.showPreview = true;
}
}https://yourapp.com/search-articles?search=some+stringcomponent extends="cbwire.models.Component" {
data = {
"search": ""
};
queryString = [ "search" ];
}component extends="cbwire.models.Component" {
// Property injection
property name="storage" inject="cbfs:disks:temp";
// Setter injection
function setStorage() inject="cbfs:disks:temp" {};
// Actions
function someAction() {
// Use the storage object
storage.create( "log.txt", "CBWIRE rocks!" );
// Use getInstance() to get objects.
var defaultStorage = getInstance( "cbfs:disks:default" );
}
}<div wire:init="loadTasks">
<ul>
<cfloop array="#args.tasks#" index="task">
<li>#task#</li>
</cfloop>
</ul>
</div>component extends="cbwire.models.Component" {
property name="taskService" inject="TaskService@myapp";
data = {
"tasks": []
};
function loadTasks(){
data.tasks = taskService.asMemento().get();
}
}dirtywire:target.<div>
<input wire:dirty.class="border-red-500" wire:model.lazy="foo">
</div>// File: ./config/ColdBox.cfc
component{
function configure() {
moduleSettings = {
cbwire = {
"enableTurbo": false,
"maxUploadSeconds": 5 * 60, // 5 minutes
"throwOnMissingSetterMethod" : false,
"trimStringValues": false,
"wiresLocation": "myWires",
"useComputedPropertiesProxy": false
}
};
}
}
/**
* Relocate user browser requests to other events, URLs, or URIs.
*
* @event The name of the event to relocate to, if not passed, then it will use the default event found in your configuration file.
* @queryString The query string or a struct to append, if needed. If in SES mode it will be translated to convention name value pairs
* @addToken Wether to add the tokens or not to the relocation. Default is false
* @persist What request collection keys to persist in flash RAM automatically for you
* @persistStruct A structure of key-value pairs to persist in flash RAM automatically for you
* @ssl Whether to relocate in SSL or not. You need to explicitly say TRUE or FALSE if going out from SSL. If none passed, we look at the even's SES base URL (if in SES mode)
* @baseURL Use this baseURL instead of the index.cfm that is used by default. You can use this for SSL or any full base url you would like to use. Ex: https://mysite.com/index.cfm
* @postProcessExempt Do not fire the postProcess interceptors, by default it does
* @URL The full URL you would like to relocate to instead of an event: ex: URL='http://www.google.com'
* @URI The relative URI you would like to relocate to instead of an event: ex: URI='/mypath/awesome/here'
*
* @return void
*/
function relocate(
event = "",
queryString = "",
boolean addToken = false,
persist = "",
struct persistStruct = structNew()
boolean ssl,
baseURL = "",
boolean postProcessExempt = false,
URL,
URI
)<div wire:poll.5s> <!-- poll every 5 seconds --> </div><div wire:poll="refreshTasks"></div>component extends="cbwire.models.Component" {
function refreshTasks() {}
}<cfif args.shouldPoll>
<div wire:poll="refreshTasks"></div>
<cfelse>
<div><!-- No more polling --></div>
</cfif><div>
<input wire:model="search" type="search" placeholder="Search articles...">
</div>component extends="cbwire.models.Component" {
property name="storage" inject="cbfs:disks:temp";
function onDIComplete(){
storage.create( "log.txt", "Dependency Injection Complete!" );
}
}<div>
<input wire:dirty.class.remove="bg-green-200" class="bg-green-200" wire:model.lazy="foo">
</div><div>
<span wire:dirty wire:target="foo">Updating...</span>
<input wire:model.lazy="foo">
</div><div>
<label wire:dirty.class="text-red-500" wire:target="foo">Full Name</label>
<input wire:model.lazy="foo">
</div>You can emit events from both your Wires and JavaScript. Superb!
Your Wire's HTML. Simple as that. Honestly.
Front-end testing of your UI Wires using a beautiful test API.
Wires are reactive UI elements you create. They include a powerful feature-set provided by CBWIRE.


noRender() to prevent your template from re-rendering. This can reduce the returned payload size for actions that do not change the UI. component extends="cbwire.models.Component"{
listeners = {
"taskAdded": "clearCache"
};
function clearCache(){}
}<script>
cbwire.on( 'taskAdded', task => {
console.log( 'A task was added. ');
})
</script>box install cbwire@be<!--- ./layouts/Main.cfm --->
<cfoutput>
<!DOCTYPE html>
<html lang="en">
<head>
<title>CBWIRE Example</title>
#wireStyles()#
</head>
<body>
<!--- JavasScript references below --->
#wireScripts()#
</body>
</html>
</cfoutput><!--- ./layouts/Main.cfm --->
<body>
<!--- Insert our task list wire here --->
#wire( "TaskList" )#
<!--- JavasScript references below --->
#wireScripts()#
</body><!--- ./views/someview.cfm --->
<cfoutput>
<div>#wire( "TaskList" )#</div>
</cfoutput>component extends="cbwire.models.Component" {
// No template defined.
}component extends="cbwire.models.Component" {
template = "wires/tasklist"; // This will look for ./views/wires/tasklist.cfm
// template = "path/to/tasklist"; <--- Can use folders
// template = "tasklist.cfm"; <--- Can use .cfm if you like
}component extends="cbwire.model.Component"{
// Action
function updateSession(){
// prevent UI updating
noRender();
}
}<!--- GOOD --->
<cfoutput>
<div>
<button wire:click="addTask">Add Task</button>
</div>
</cfoutput>
<!--- BAD --->
<cfoutput>
<div>
<button wire:click="addTask">Add Task</button>
</div>
<div>
<h1>Tasks</h1>
</div>
</cfoutput>component extends="cbwire.models.BaseWireTest" {
function run(){
describe( "TaskList.cfc", function(){
it( "calling 'clearTasks' removes the tasks", function() {
wire( "TaskList" )
// Sets our data properties
.data( "tasks", [ "task1" ] )
// Verifies output in our template rendering
.see( "<li>task 1</li>" )
// Runs the 'clearTasks' action
.call( "clearTasks" )
// Verifies updated template rendering
.dontSee( "<li>task 1</li>" );
} );
} );
}
}wire( "TaskList" )
.data( "tasks", [ "task1", "task2" ] ) // one property at a time
.data( { "tasks" : [ "task1", "task2" ] } ); // struct of propertieswire( "TaskList" )
.computed( "count", function() { return 3; } ) // one property at a time
.computed( { "count" : function() { return 3; } } ); // struct of propertieswire( "TaskList" ).toggle( "showModal" );wire( "TaskList" ).call( "clearTasks" );
wire( "TaskList" ).call( "clearTasks", [ param1, param2 ] );wire( "TaskList" ).emit( "addedTask" );
wire( "TaskList" ).emit( "addedTask", [ param1, param2 ] );wire( "TaskList" ).see( "<h1>My Tasks</h1>" );wire( "TaskList" ).dontSee( "<h1>Someone Else's Tasks</h1> ");wire( "TaskList" ).seeData( "tasksRemoved", true );wire( "TaskList" ).dontSeeData( "tasksRemoved", true );// File: wires/TaskList.cfc
component extends="cbwire.models.Component" {
// Data properties
data = {
"task": "",
"tasks": []
};
// Computed properties
computed = {
"counter": function() {
return arrayLen( data.tasks );
}
};
// Listeners
listeners = {
"taskAdded": "sendEmail"
};
// Action
function addTask(){
data.tasks.append( data.task );
this.emit( "taskAdded", data.task );
}
}<!--- /views/wires/tasklist.cfm --->
<cfoutput>
<div>
<div>Number of tasks: #args.computed.counter()#</div>
<div><input wire:model="task"></div>
<div><button wire:click="addTask">Add Task</button></div>
</div>
</cfoutput>install commandbox-cbwirecbwire create Counter
// Created /wires/Counter.cfc
// Created /views/wires/counter.cfm
// Created /tests/specs/integration/wires/CounterTest.cfccbwire create name=Counter views=false
// Created /wires/Counter.cfc
// Created /tests/specs/integration/wires/CounterTest.cfcCreate dynamic properties for your UI Wires.
Create dynamic properties for your UI Wires.
component extends="cbwire.models.Component"{
this.constraints = {
"task": { required: true }
};
// Data Properties
data = {
"task": ""
};
}component extends="cbwire.models.Component"{
// Action
function addTask() {
var result = validate(); // ValidateResult object
if ( !result.hasErrors() ) {
queryExecute( ... );
}
}
}Help website users during wait times using Loading States.
component extends="cbwire.models.Component"{
function addTask(){
sleep( 5000 ); // Optimize this code yo!
}
}<div>
<button wire:click="addTask">Add Task</button>
<div wire:loading>
Adding Task...
</div>
</div><div>
<button wire:click="addTask" wire:loading.attr="disabled">
Add Task
</button>
</div>Seamlessly connect between the front-end and your server back-end using JavaScript and CFML.
Define reactive properties for your UI Wires with just a few lines of code.
component extends="cbwire.models.Component" {
property name="taskService" inject="taskService@myapp";
// Computed Properties
computed = {
"allTasks": function() {
return taskService.getAll();
}
};
}
component extends="cbwire.models.Component" {
property name="taskService" inject="taskService@myapp";
// Computed Properties
computed = {
"allTasks": function() {
return taskService.getAll();
}
};
// Action
function deleteTasks() {
// Notice below we don't invoke the method using computed.allTasks().
// We instead just use 'computed.allTasks'.
// At this point, cbwire has rendered our computed properties
// and overwritten the references with their result.
if ( arrayLen( computed.allTasks ) {
taskService.deleteAll();
}
}
}component extends="cbwire.models.Component" {
property name="taskService" inject="taskService@myapp";
// Computed Properties
computed = {
"allTasks": function() {
return taskService.getAll();
}
};
// Action
function deleteTasks() {
if ( arrayLen( this.getAllTasks() ) {
taskService.deleteAll();
}
}
}<cfoutput>
<div>
<ul>
<cfloop array="#args.allTasks#" index="task">
<li>#task#</li>
</cfloop>
</ul>
</div>
</cfoutput>component extends="cbwire.models.Component" {
data = {
"taskId": 0
};
function onMount( event, rc, prc, parameters ){
data.taskId = event.getValue( "taskId" );
}
}
component extends="cbwire.models.Component" {
data = {
"hydrated": false
};
function clicked() {}
function onHydrate( data ) {
// Note that computed properties are not rendered yet
data.hydrated = true;
}
function onRender( args ) {
return "
<div>
<div>Hydrated: #args.hydrated#</div>
<button wire:click='clicked'>Click me</button>
</div>
";
}
}component extends="cbwire.models.Component" {
data = {
"count": 1
};
function onHydrateCount( data ) {
// Note that computed properties are not rendered yet
data.count += 1;
}
}component extends="cbwire.models.Component" {
data = {
"rendered": true
};
computed = {
"currentDate": function() {
return now();
}
}
function onRender( args ) {
return "
<div>
<div>Rendered: #args.rendered# at #args.currentDate#</div>
</div>
";
}
}component extends="cbwire.models.Component" {
property name="taskService" inject="taskService@myapp";
// Computed Properties
computed = {
"allTasks": function() {
return taskService.getAll();
}
};
}
component extends="cbwire.models.Component" {
// Computed Properties
computed = {
"allTasks": function() {
// return something here
}
};
// Action
function deleteTasks() {
if ( arrayLen( computed.allTasks() ) {
taskService.deleteAll();
}
}
}<cfoutput>
<div>
<ul>
<cfloop array="#args.computed.allTasks()#" index="task">
<li>#task#</li>
</cfloop>
</ul>
</div>
</cfoutput><!--- modules_app/contentbox-custom/_themes/cbwireTheme/layouts/blog.cfm --->
<cfoutput>
<!--- Global Layout Arguments --->
<cfparam name="args.print" default="false">
<cfparam name="args.sidebar" default="true">
<!DOCTYPE html>
<html lang="en">
<head>
<!--- Page Includes --->
#cb.quickView( "_blogIncludes" )#
<!--- ContentBoxEvent --->
#cb.event( "cbui_beforeHeadEnd" )#
<!--- PULL IN CBWIRE STYLING --->
#wireStyles()#
</head>
<body>
<!--- ContentBoxEvent --->
#cb.event( "cbui_afterBodyStart" )#
<!--- Header --->
#cb.quickView( '_header' )#
<!--- ContentBoxEvent --->
#cb.event( "cbui_beforeContent" )#
<!--- Main View --->
#cb.mainView( args=args )#
<!--- INCLUDE REACTIVE NEWSLETTER SIGNUP COMPONENT --->
#wire( "NewsletterSignup", {
validate: true
} )#
<!--- ContentBoxEvent --->
#cb.event( "cbui_afterContent" )#
#cb.quickView( view='_footer' )#
<!--- ContentBoxEvent --->
#cb.event( "cbui_beforeBodyEnd" )#
<!--- PULL IN CBWIRE JAVASCRIPT ASSETS --->
#wireScripts()#
</body>
</html>
</cfoutput><!--- modules_app/contentbox-custom/_themes/cbwireTheme/views/index.cfm --->
<div class="row">
<div class="col-12">
#wire( "ContactForm" )#
</div>
</div>component extends="cbwire.models.Component"{
// Action
function addTask() {
validateOrFail();
queryExecute( ... );
}
}component extends="cbwire.models.Component"{
// Action
function addTask() {
var data = { "email": "cbwire@rocks.com" };
var validationManager = getValidationManager();
var result = validationManager.validate(
target=data,
constraints={
"email": { required: true, type: "email" }
}
);
}
}<cfoutput>
<div>
<input wire:model="task" type="text">
<button wire:click="addTask">Add</button>
<cfif args.validation.hasErrors( "task" )>
<cfloop array="#args.validation.getAllErrors( "task" )#" index="error">
<div>#error#</div>
</cfloop>
</cfif>
</div>
</cfoutput><div>
<button wire:click="addTask">Add Task</button>
<div wire:loading wire:target="addTask">
Adding task...
</div>
</div><div wire:loading.delay>...</div><div wire:loading.delay.shortest>...</div> <!-- 50ms -->
<div wire:loading.delay.shorter>...</div> <!-- 100ms -->
<div wire:loading.delay.short>...</div> <!-- 150ms -->
<div wire:loading.delay>...</div> <!-- 200ms -->
<div wire:loading.delay.long>...</div> <!-- 300ms -->
<div wire:loading.delay.longer>...</div> <!-- 500ms -->
<div wire:loading.delay.longest>...</div> <!-- 1000ms --><div wire:loading.flex>...</div>
<div wire:loading.grid>...</div>
<div wire:loading.inline>...</div>
<div wire:loading.table>...</div><div>
<button wire:click="addTask">Add Task</button>
<div wire:loading.remove>
Click 'Add Task'
</div>
</div><script>
document.addEventListener("DOMContentLoaded", () => {
cbwire.hook('component.initialized', (wire) => {})
cbwire.hook('element.initialized', (el, wire) => {})
cbwire.hook('element.updating', (fromEl, toEl, wire) => {})
cbwire.hook('element.updated', (el, wire) => {})
cbwire.hook('element.removed', (el, wire) => {})
cbwire.hook('message.sent', (message, wire) => {})
cbwire.hook('message.failed', (message, wire) => {})
cbwire.hook('message.received', (message, wire) => {})
cbwire.hook('message.processed', (message, wire) => {})
});
</script><script>
document.addEventListener("livewire:load", function() {
var thisWire = cbwire.find('#args._id#'); // args._id contains the id of our wire
var count = thisWire.count; // gets the value of a data property called 'count'
thisWire.count = 5; // updates the values of a data property
thisWire.increment(); // calls the increment action on our wire
thisWire.addTask( 'someTask' ); // calls the addTask action and passes parameters
thisWire.call( 'increment' ); // same as increment call above
// On someEvent, console log
thisWire.on( 'someEvent', function() {
console.log('Got someEvent');
} );
thisWire.emit('someEvent', 'foo', 'bar'); // emits someEvent and pass parameters
} );
</script>args.[propertyName].component extends="cbwire.models.Component"{
data = {
"task": ""
};
}component extends="cbwire.models.Component"{
property name="taskService" inject="TaskService@myapp";
// Data properties
data = {
"task": ""
};
// Action
function addTask(){
taskService.create( data.task );
}
}component extends="cbwire.models.Component"{
property name="taskService" inject="TaskService@myapp";
// Data properties
data = {
"task": ""
};
// Action
function addTask(){
taskService.create( this.getTask() );
}
}<cfoutput>
<div>
<h1>Task</h1>
<div>#args.task#</div>
</div>
</cfoutput>component extends="cbwire.models.Component"{
// Data properties
data = {
"task": "Some task here"
};
// Action
function resetForm() {
data.task = "Another value";
reset( "task" ); // Reverts data.task to 'Some task here'
}
}function someAction() {
reset( "task" ); // resets 'task' data property
reset( [ "task", "anotherprop" ] ); // reset multiple properties at once
reset(); // resets all properties
}The magic sauce. Actions allow you to easily listen to page interactions and call a method on your Wires.
component extends="cbwire.models.Component" {
data = {
"photo": "",
"error": ""
};
function save() {
// 'photo' is now an instance of FileUpload@cbwire
if ( data.photo.getSize() > "100000" ) {
data.error = "Photo upload too big!";
} else {
// Store our file locally
fileWrite( expandPath( "./somewhere.jpg" ), data.photo.get() );
// Delete our file from CBWIRE's temporary storage and reset 'photo' data property
data.photo.destroy();
}
}
}<form wire:submit.prevent="save">
File: <input type="file" wire:model="photo">
<div><button type="submit">Save</button></div>
<!--- Show thumbnail --->
<cfif isObject( args.photo ) and args.photo.isImage()>
<div><img src="#args.photo.getPreviewURL()#"></div>
</cfif>
<cfif len( args.error ) >
<div class="error">#args.error#</div>
</cfif>
</form>Remove full-page reloads from your applications and create single-page applications with ease using Turbo.
<a href="" wire:click.prevent="doSomething">Do Something</a>
<button wire:click="doSomething">Do Something</button>
<input wire:keydown.enter="doSomething">
<form wire:submit.prevent="save">
<button>Save</button>
</form><button wire:foo="someAction"><button wire:click="addTask('Some Task')">Add Task</button>// Action
function addTask( taskName ){
} data = {
"task": ""
};
// Actions
function clearTasks(){
data.task = "";
} // Computed Properties
computed = {
"tasks": function() {
return queryExecute( "
select *
from tasks
" );
}
};
// Actions
function deleteTasks(){
if ( computed.tasks.recordCount ) {
// query to delete the tasks...
}
}<div>
#args.message#
<button wire:click="setMessageToHello">Say Hi</button>
</div><div>
#args.message#
<button wire:click="$set( 'message', 'Hello' )">Say Hi</button>
</div>function someAction() {
emit( "some-event" );
}component {
listeners = {
"some-event": "$refresh"
};
}<input type="file" wire:model="photo">
<div wire:loading wire:target="photo">Uploading...</div>// File: ./config/ColdBox.cfc
component{
function configure() {
moduleSettings = {
"cbwire" = {
"enableTurbo": true
}
};
}
}
npm i @hotwired/turboimport * as Turbo from "@hotwired/turbo"<script type="module">
import hotwiredTurbo from 'https://cdn.skypack.dev/@hotwired/turbo';
</script> #wireScripts()#
<script src="https://cdn.jsdelivr.net/gh/livewire/turbolinks@v0.1.x/dist/livewire-turbolinks.js" data-turbolinks-eval="false" data-turbo-eval="false"></script>
</body>.turbo-progress-bar {
height: 5px;
background-color: green;
}<!-- ./layouts/Main.cfm -->
<cfoutput>
<html>
<head>
<title>Turbo Page</title>
#wireStyles()#
</head>
<body>
<div class="turbo-progress-bar"></div>
<div class="mainContent">
Some content here
</div>
#wireScripts()#
</body>
</html>
</cfoutput><a href="/" data-turbo-preload>Home</a>CBWIRE's powerful directives allow you to listen for client side events, invoke actions, bind to data properties, and more.
<form wire:submit.prevent="someAction">
<button type="submit">Submit Form</button>
</form><input wire:keydown="foo" type="text"><input wire:keydown.enter="foo" type="text"><select wire:foo="bar"></select><input wire:model="foo" type="text"><input wire:model.debounce.1s="foo" type="text">
<input wire:model.debounce.500ms="foo" type="text"><input wire:model.defer="foo" type="text"><input wire:model.lazy="foo" type="text"><div wire:poll></div>
<div wire:poll.5s></div>
<div wire:poll.5000ms></div>
<div wire:poll.5s="fooMethod"></div><div wire:init="foo"></div><span wire:loading><img src="spinner.gif"></span><div wire:loading.class="highlight"></div><div wire:loading.class.remove="highlight" class="highlight"></div><button wire:loading.attr="disabled">...</button><div wire:dirty="foo">...</div><div wire:dirty.class="highlight">...</div><div wire:dirty.class.remove="highlight" class="highlight">...</div><button wire:dirty.attr="disabled">...</div><button wire:dirty.attr="disabled" wire:target="foo">...</div><div wire:ignore></div><div wire:ignore.self></div><a href="" wire:click.prevent="someAction">Click here</a><input wire:keydown.page-down="foo">