Developing AlpineJs Apps Similar to Vue's Composition API
Vue’s primary motivation behind Composition API’s introduction was a cost-free mechanism for reusing logic between multiple components or apps. This idea is based on Functional Composition for programming, and its simplest form is about to produce a function by combining multiple other functions.
This technique is effortless to apply in AlpineJs using a combination of
x-init attributes. First of all, if you never heard of this framework before, check out my introduction AlpineJs article.
Let’s have as a base example the Netlify Contact Form described in the previous article. We will reshape the code to separate logic into different modules. The following figure depicts the new architecture.
We separate the code into three modules. One module will validate the form input utilizing another module that contains input validators, and an additional module will take care of the form backend submission.
Let’s briefly explain the depicted modules. The most simple module is the
validators. It contains functions that validate if input value follows a corresponding rule. It includes four self-explanatory validators (
hasMaxLength) and the code is:
netlifySubmission module handles the backend submission to the netlify service. It exposes a function named
submitToNetlify that takes one argument, the form element intended to submit.
The code is taken from the previous article, modified for our module:
formValidator module carries out the main work. It has six responsive data attributes that correspond to name, email, and comment input components that compose the submit form. The dirty boolean variables are needed to determine when a user has lost focus from the corresponding input component. It is used to prevent the error messages to appear before the user attempts to enter a value. It is used in the HTML like this:
And it produces the following behavior:
The full code for the
formValidator module is:
Worth noticing is the
formValidator_init function, which is executed during the
x-init lifecycle, and define the backend submission function.
The above architecture provides a lot of flexibility with the code. For example, to change the backend, only the
netlifySubmission module will need to be replaced without any other code modification. Or maybe use the validators to other form inputs in the app.
Therefore, the final step is to create the AlpineJs component by using the previous modules. The Alpine.js expects a data object in its
x-data attribute, which also declares the component scope; accordingly, a function may compose this data object by combining the modules' exported functions and variables.
If it helps, you may think of this function as the
setup property of a Vue component, although you cannot use the full capabilities that Vue’s
setup function provides, for example, an
watch feature. For that, the x-init attribute and the $watch magic attribute may be of help. We write inside the HTML file that the form lies the following code:
First, we defined a script tag as a
module and imported our other modules. In our example, we used the
.mjs extension, although the standard
.js extension will work as well. I recommend using the
We must then explicitly assign our function in the
window global scope to be accessible from the
formValidator module in the
init() function is used in x-init attribute and is executed after the initialization of AlpineJs component. It mimics the
ngOnInit() hooks from Vue or Angular, respectively. In this function, we may initialize our modules as we do with the
formValidator or include some side effects using $watch magic attribute.
Overall, I find this approach very scalable if you want to grow your AlpineJs to something more complicated or reduce some code repetition to your web app.
You may find a complete working version in the following codesandbox: