Security News
RubyGems.org Adds New Maintainer Role
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
@ryangjchandler/spruce
Advanced tools
✨ Help to support the maintenance of this package by sponsoring me.
A lightweight state management layer for Alpine.js
Many large frameworks have their own state management solutions. One thing these libraries have in common is a higher level of complexity and a wide range of API interactions. Since Alpine.js is designed to be a simpler and lighter alternative to larger frameworks such as Vue and React, shouldn't the state management solution be as simple, if not simpler, too?
Include the following <script>
tag in the <head>
of your document:
<script src="https://cdn.jsdelivr.net/gh/ryangjchandler/spruce@0.x.x/dist/spruce.umd.js"></script>
Important: This must be added before loading Alpine.js when using CDN links.
If you wish to include Spruce with your own bundle:
yarn add spruce.js
or:
npm install spruce.js --save
Then add the following to your script:
import Spruce from '@ryangjchandler/spruce'
To verify you have correctly installed Spruce, copy & paste the following code snippet into your project.
<div x-data>
<div x-show="$store.modal.open === 'login'">
<p>
This "login" modal isn't built with a11y in mind, don't actually use it
</p>
</div>
</div>
<div x-data>
<div x-show="$store.modal.open === 'register'">
<p>
This "register" modal isn't built with a11y in mind, don't actually use it
</p>
</div>
</div>
<div x-data>
<select x-model="$store.modal.open">
<option value="login" selected>login</option>
<option value="register">register</option>
</select>
</div>
<script>
Spruce.store('modal', {
open: 'login',
});
</script>
To see what the code should do, use this CodePen. Thanks @HugoDF!
Spruce exposes less than a handful of possible interaction points. There is an extremely simple "subscriptions" interaction which connects the roots from your Alpine component to the global store, then there is the "stores" interaction which allows you to define scopes of global state for use throughout your components.
If you are using the CDN build, you can interact with Spruce using the window.Spruce
variable:
<script>
Spruce.store('modals', {
open: 'login',
})
</script>
If you are importing Spruce into your own bundle, you can interact with it like any other variable:
store.js
import Spruce from '@ryangjchandler/spruce'
Spruce.store('modals', {
open: 'login'
})
export default Spruce
app.js
import './store'
import 'alpinejs'
Note: You must
import
your store before Alpine.
Spruce hooks into Alpine using the "magic properties" API, meaning there are no extra steps needed. Start using the $store
variable in your components right away.
<div x-data="{}">
<span x-text="$store.application.name"></span>
</div>
To define a piece of global state, you can use the Spruce.store()
method:
Spruce.store('application', {
name: 'Amazing Alpine Application'
})
The first argument defines the top level property of the scope. The second argument defines the state for the scope, it could be a string, integer or object with nested properties.
To access the name
property, you can do the following inside of your component:
<div x-data="{}">
<span x-text="$store.application.name"></span>
</div>
The <span>
will now have "Amazing Alpine Application" set as its innerText
.
You can modify your global state from external scripts using the Spruce.store()
method too:
Spruce.store('application', {
name: 'Amazing Alpine Application'
})
Spruce.store('application').name = 'Amazing Spruce Integration'
This will trigger Alpine to re-evaluate your subscribed components and re-render.
A Spruce.reset()
method is provided so that you can completely overwrite a global store:
Spruce.store('application', {
name: 'Amazing Alpine Application'
})
Spruce.reset('application', {
name: 'Reset Application'
})
Calling the reset
method will make the new state reactive and cause subscribed components to re-render.
You can register watchers in a similar fashion to Alpine. All you need is the full dot-notation representation of your piece of state and a callback.
Spruce.store('form', {
name: 'Ryan',
email: 'support@ryangjchandler.co.uk'
})
Spruce.watch('form.email', (old, next) => {
// do something with the values here
})
In the above snippet, when we change the value of form.email
either from a component or externally in a separate JavaScript file, our callback will be invoked and will receive the old value, as well as the new value. This can be useful for running automatic inline validation when a property changes, or triggering an action elsewhere in another component without the need for dispatching events.
Note: you can get stuck in an watch loop if you're updating other store properties that also have watchers defined.
Spruce ships with a basic event bus. It exposes two methods:
Spruce.on(eventName, callback)
- this can be used to register an event listener. This will react to any internal events, such as init
. Your callback will receive a single detail
property which contains any data sent from the event, as well as the global store.Spruce.on('init', ({ store }) => {
// do something with the store here...
})
Spruce.once(eventName, callback)
- this can be used to register an event listener that is only run a single time. This is useful for one time events, such as fetching HTML from the server when hovering over a button or similar.Spruce.once('event', () => {
// do something once...
})
Spruce.off(eventName, callback)
- this can be used to unhook or de-register an event listener.var callback = () => {}
Spruce.off('init', callback)
You can also unhook a listener using the function returned by Spruce.on()
. This is especially useful for anonymous function callbacks.
var off = Spruce.on('event', () => {})
off()
Spruce.emit(eventName, data = {})
- this can be used to emit an event. The first argument should be the name of the event, the second should be an object containing data. This will be merged in with the core data, which consists of a store
property. When emitting an event, a browser event will also be dispatched with a spruce:
prefix.Spruce.emit('event-name', {
foo: 'bar'
})
In the example above, a spruce:event-name
event will be fired on the window
level, so you could register an event listener inside of your Alpine component:
<div x-data @spruce:event-name.window="foo = $event.detail.store.foo">
</div>
This projects follow the Semantic Versioning guidelines. This means that there could be breaking changes on minor version changes, up until v1.x is reached.
For example, 0.1 -> 0.2 might introduce a breaking change.
Copyright (c) 2020 Ryan Chandler and contributors
Licensed under the MIT license, see LICENSE.md for details.
FAQs
A lightweight state management layer for Alpine.js
The npm package @ryangjchandler/spruce receives a total of 1,003 weekly downloads. As such, @ryangjchandler/spruce popularity was classified as popular.
We found that @ryangjchandler/spruce demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.
Security News
Research
Socket's threat research team has detected five malicious npm packages targeting Roblox developers, deploying malware to steal credentials and personal data.