(Updated: )
/ #alpinejs #javascript 

How to Access Alpine.js Magic Properties from inline handlers and function/component methods

Alpine.js magic properties are crucial to leveraging its best features.

When using Alpine.js in a “mainly markup” configuration (no script tags), the magic properties tend to be accessible seamlessly.

Alpine.js magic properties are as follows:

  • $el: the element to which an Alpine.js component is bound (also called the root element)
  • $refs: references to DOM Nodes as defined in the markup for an Alpine.js component
  • $event: (only exists in templates) when adding an event handler, using $event will pass the full event into the handler.
  • $dispatch: dispatch a custom event see A guide to Alpine.js component communication
  • $nextTick: delay running of a function (callback) until after Alpine.js has updated/rendered
  • $watch: run a function (callback) on every change to a reactive property.
Table of Contents

Access to magic properties in Alpine.js templates

There’s no official Alpine.js “template” definition but I’ll call all the elements inside of the root element (the one that has the x-data attribute), the “template”.

In your Alpine.js template, you can access the magic properties directly.

For example:

<div x-data="{}" x-init="console.log($el)">
  <button @click="$dispatch('toggle')">Toggle</button>
</div>

The x-init inline handler uses the $el magic property (& logs it out).

The @click inline handler uses the $dispatch magic property to dispatch a toggle custom event when the “Toggle” button is clicked.

This example is lifted from A guide to Alpine.js component communication, head over there for more in-depth use-cases for $dispatch.

We’ve now seen how to access magic properties in inline-handlers.

Next we’ll look at the state of magic property access on this in Alpine.js “function components”.

Magic properties on this in JavaScript function components in Alpine.js

Alpine.js “function components” are components that define x-data as a function call eg. x-data="someFunction()". Which allows you to define your component logic in a script tag (as long as it exposes a function on window).

This is in contrast using the inline x-data object definition eg. x-data="{ isOpen: false }" and inline handlers such as @click="isOpen = !isOpen.

Given the following component with inline handlers:

<div x-data="{ isOpen: false }">
  <p x-show="isOpen">
    What you can toggle
  </p>
  <button @click="isOpen = !isOpen">
    Toggle
  </button>
</div>

We can write it as a function component as follows. Note how we’ve extracted the isOpen toggling to a function and how the component function returns initial state for x-data and the functions needed in the component (in this case toggle).

<div x-data="component()">
  <p x-show="isOpen">
    What you can toggle
  </p>
  <button @click="toggle()">
    Toggle
  </button>
</div>
<script>
  function component() {
    return {
      isOpen: false,
      toggle() {
        this.isOpen = !this.isOpen;
      }
    }
  }
</script>

From the previous section we know that in inline handlers, we can access magic properties simply by referring to them.

It would be reasonable to expect that magic properties are available on this just as they are in inline handlers. This is not the case.

See the following table for reference (up to date as of Alpine.js v2.3.x). A runnable version is available at Alpine.js Magic Properties on this - Alpine.js Playground.

The table shows that:

  • no magic properties are available for the x-data handler, this makes sense since x-data is run before the component context (this) exists
  • for all other directive handlers (x-init, x-text, x-html, x-bind, x-show, x-on): $el, $refs, $nextTick, $watch are accessible on this. $event and $dispatch are not available on this.
typeavailable magic properties on thisunavailable magic properties on this
x-data handlernone$el, $refs, $event, $dispatch, $nextTick, $watch
x-init handler$el, $refs, $nextTick, $watch$event, $dispatch
x-text handler$el, $refs, $nextTick, $watch$event, $dispatch
x-html handler$el, $refs, $nextTick, $watch$event, $dispatch
x-bind handler$el, $refs, $nextTick, $watch$event, $dispatch
x-show handler$el, $refs, $nextTick, $watch$event, $dispatch
x-on handler$el, $refs, $nextTick, $watch$event, $dispatch

You can for example in an x-init handler use the $watch magic property this.$watch('foo', (val) => console.log(val)) and so on for other types of directive handlers and magic properties that are available on this (this.$el, this.$refs, this.$nextTick, this.$watch).

We’ve now seen what a function component is and which magic properties are available on this.

Next we’ll see how to access $dispatch in x-init by passing it from the template.

Access $dispatch magic property in x-init by passing it from the template.

The following is an example of how one would access $dispatch in the handler for x-init in an Alpine.js function component.

We can pass $dispatch into the function from the template, it can then be used without issue.

<div
  x-data="page()"
  x-init="init($dispatch)"
>
  <p x-text="output"></p>
</div>
<script>
  function page() {
    return {
      output: '',
      init($dispatch) {
        // this.$dispatch is **not** defined in x-init handler
        this.output += "Calling $dispatch";
        $dispatch('test');
        this.output += "\nCalled $dispatch";
      },
    };
  }
</script>

You can see the full example at Alpine.js Magic Property access in JavaScript - Alpine.js Playground.

We’ve now see how to pass $dispatch into x-init function handlers from the template.

Next we’ll see how to access $event and $dispatch in the case of Alpine.js x-on function handlers.

Access $event and $dispatch in x-on handlers by passing it from the template

Note this approach of passing $dispatch from the template will work for handlers for other directives, x-on handlers are the most likely to need to use $dispatch.

The following example is an Alpine.js function component setup which contains a button with value="button-value", on click, we would like to be able to read the event.target.value and dispatch a custom event.

In order to achieve this, we pass the $event and $dispatch magic properties into the handleClick handler. This makes them available as parameters for handleClick.

<div
  x-data="page()"
>
  <p x-text="output"></p>
  <button
    @click="handleClick($event, $dispatch)"
    value="button-value"
  >
    Get $event and $dispatch on click
  </button>
</div>
<script>
  function page() {
    return {
      output: '',
      handleClick(e, $dispatch) {
        // this.$event and this.$dispatch are **not** defined in x-on handler
        this.output += `\nevent.target.value: "${e.target.value}"`;
        this.output += "\nCalling $dispatch";
        $dispatch("test2");
        this.output += "\nCalled $dispatch";
      },
    };
  }
</script>

You can see the full example at Alpine.js Magic Property access in JavaScript - Alpine.js Playground.

We’ve now seen how to access $event and $dispatch magic properties in an x-on function handler.

Through this post you should now have knowledge of:

  • what magic properties are
  • how to access them in the template
  • which magic properties are available on this for function components/handlers (this.$el, this.$refs, this.$nextTick, this.$watch)
  • how to pass magic properties which are not available on this to function handlers from the template ($event and $dispatch)

That’s it for this post, you can check out the Alpine.js tag on Code with Hugo for more in-depth Alpine.js guides.

If you’re interested in Alpine.js, Subscribe to Alpine.js Weekly. A free, once–weekly email roundup of Alpine.js news and articles.

Photo by Aziz Acharki on Unsplash

Author

Hugo Di Francesco

Co-author of "Professional JavaScript", "Front-End Development Projects with Vue.js" with Packt, "The Jest Handbook" (self-published). Hugo runs the Code with Hugo website helping over 100,000 developers every month and holds an MEng in Mathematical Computation from University College London (UCL). He has used JavaScript extensively to create scalable and performant platforms at companies such as Canon, Elsevier and (currently) Eurostar.

Interested in Alpine.js?

Subscribe to Alpine.js Weekly. A free, once–weekly email roundup of Alpine.js news and articles