stimulus-bind v0.9.0
Use Stimulusjs with simple, one-way data binding to reduce boilerplate code.
It is just a 3k file when gzipped.
Usage
Include in your project:
yarn add stimulus-bind jsep stimulus
# or
npm i stimulus-bind jsep stimulus --saveOr use the stimulus.umd.js directly (which can expose a StimulusBind in global).
Template
<div data-controller="foo_controller">
<label>
My Name is
<input type="text" data-action="input->foo_controller#nameChanged" data-target="my_name_input">
</label>
<div style="color:blue;" data-target="my_name_greet"/>
<div style="color:red;" data-target="my_name_error">name is empty</div>
</div>The use of data-controller, data-action and data-target is the same as in stimulus guide.
The JS is a bit different:
import StimulusBind from 'stimulus-bind'
class MyController extends StimulusBind {
nameChanged() {
this.myName = this.ref('my_name_input').value
}
}
StimulusBind.register('foo_controller', MyController, {
my_name_greet: {text: '"Hello, " + myName', if: 'myName'},
my_name_error: {if: '!myName'}
})- Inherit the controller from
StimulusBindinstead ofstimulus.Controller. - Do not set the
targetsfield in controller, instead, set bindings when registering. registerunder StimulusBind, a global app will be created when needed.ref(targetName)returns the first target in or not in DOM (including the ones hidden byifbinders).refs(targetname)returns all targets, including the ones detached byifbinders.
The binding data is in the format of:
{
{targetName}: {{binder1}: {bindValueExpression1}, {binder2}: {bindValueExpression2}}
}All bindings are one-way binding.
When the dependent data changed, targets with binders will react the update at once.
Expressions must a string of simple js expression that jsep can parse. In the expression we allow:
- function calls
- operators
- getting properties
And the framework will compute what values does this expression depend on and do a minimal update when neccessary.
Binders
valuevalue of input elementcheckedcheckbox or radio is checkeddisableddisabled based on an expressiontextbinds content text to an expressionhtmlbinds inner html to an expressionstyle-*binds extra style name on a value, for example:notification_bar: {'style-color': 'error ? "white" : "black"', 'style-background-color': 'error ? "red" : "yellow"'}.class-*binds extra class name on a value, for example:foo: {'class-flat': 'useFlatTheme'}ifelement exist or not, based on the expression value
Other binders like src, href, ... will reflect to element attributes.
There is no each binder, the complexity of rendering each binders is beyond what a simple data binding framework can do.
Caveats
It is a very simple data binding, elements won't refresh if you change nested data like: this.someData.foo = bar.
Instead, you can: this.someData.foo = bar; this.someData = this.someData; // triggers update.
You can only use instance method in a function call in the binder expression, and the method must be pure.