Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

$dispatch and $watch

Components on a page are independent, but they need to talk: a form tells the list about a new task, a counter learns about a deletion. For that, $dispatch sends a custom browser event. And $watch lets you run code when a specific property changes.

$dispatch

$dispatch('name', data) creates an event on the current element. The event bubbles up the tree, and a parent catches it with a regular @name:

<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
 
<div x-data="{ last: '' }" @task-added="last = $event.detail.title">
  <button @click="$dispatch('task-added', { title: 'Buy milk' })">Add</button>
  <p x-text="'Last task: ' + last"></p>
</div>

After a click the page shows "Last task: Buy milk". The data you passed is in $event.detail.

Sibling components

If components are not nested, the event never bubbles to the sibling. Then listen on the whole window with the .window modifier:

<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
 
<form x-data="{ title: '' }" @submit.prevent="$dispatch('task-added', { title }); title = ''">
  <input x-model="title" placeholder="New task">
  <button>Add</button>
</form>
 
<ul x-data="{ tasks: [] }" @task-added.window="tasks.push($event.detail.title)">
  <template x-for="task in tasks">
    <li x-text="task"></li>
  </template>
</ul>

The form and the list are two separate components. The form sends the event, the list catches it on window and adds the task. { title } is the short form of { title: title }.

$watch

$watch('property', callback) calls the function every time the property changes. The function gets the new and the old value. Watching is usually set up in init():

<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
 
<div x-data="{
  filter: 'all',
  init() {
    this.$watch('filter', (value, oldValue) => {
      console.log('Filter:', oldValue, '→', value);
    });
  }
}">
  <select x-model="filter">
    <option value="all">All</option>
    <option value="active">Active</option>
    <option value="done">Done</option>
  </select>
</div>

Pick "Active" and the console shows "Filter: all → active". The function is not called on start, only on changes.

$watch or x-effect

$watchx-effect
What it watchesOne named propertyEverything read in the expression
Runs on startNoYes
Old valueYesNo

Common mistake

Naming an event in camelCase: $dispatch('taskAdded') and @taskAdded. HTML lowercases attribute names, so Alpine listens for taskadded, which nobody sends. Name events with hyphens: task-added.

Thanks for reading!

Donate to the site — we work hard for you.