Comfey is a tiny data binding library inspired by React hook useState. Read more in Comfey Wiki
- 0 dependencies / Lightweight v1.0.0 is 35 lines of code
- Looks like react hook useState
- No loops, event driven
- Simple HTML5 data attributes to bind the html elements to the states
- Finite State Machine as a plugin
Using NPM
npm install comfey
Using Yarn
yarn add comfey
Instantiate Comfey
- optionally passing a DOMElement
as root of the component.
By default document
will be used.
import Comfey from 'comfey';
const myComponent = new Comfey(document.getElementById('my-component'));
Use .useState()
method to initialize a state, useState()
accepts 3 parameters.
Returns getter and setter functions
/**
*
* @param {string} state name of the state
* @param {any} initialVal initial value
* @param {function} watcher watcher function that will be called everytime the value of the state changes
*
* @returns {Array<[function, function]>} getterFunction and SetterFunction
*/
Example:
const [count, setCount] = app.useState('count', 3, countWatcher);
Watch gets newValue
, oldValue
and name
of the state and is invoked everytime the state changes.
function countWatcher(newVal, oldVal, stateName) {
// Do something when value of count state changes
}
You can have any number of applications on a page. Instanciate a new Comfey whenever you need.
- Multiple app can use duplicate state names
- they will be scoped within each app
- You will have to scope your javascript as well
- Just avoid declaring getters and setters globally
Example
// scoped code blocks can use same getters / setters, etc names if desired.
// name uniquely if needs to be in the same scope
(() => {
const app = new Comfey(document.getElementById('app1'), COMFEY_DEBUG);
const [, setActive] = app.useState('stateActive', false);
setInterval(() => {
setActive(Math.random() > 0.5);
}, 1000);
})();
(() => {
const app = new Comfey(document.getElementById('app2'), COMFEY_DEBUG);
const [, setActive] = app.useState('stateActive', false);
setInterval(() => {
setActive(Math.random() > 0.5);
}, 1000);
})();
Use data-bind
attribute with stateName as its value to bind the innerHTML
of the element to the state's value.
data-bind-visible
data-bind-hidden
Bind visible and hidden accepts value to compare.
Example
data-bind-visible="numberStatus::medium"
means the element will be visible if the state numberStatus
is set to medium
value.
data-bind-class
Bind class accepts value to compare, but will not interpolate the bound value as a classname.
Example
data-bind-class="currentPage::active::home"
means the element will get active class if the state currentPage
is set to home
value.
You can bind an attribute to a state's value w/ data-bind-attr
. Data bind attributes can take values delimited by ::
which will make each delimited string an argument. The argument pattern looks like
<state>::<dynamic-attr>::<value>
Example:
data-bind-attr="count::style::font-size: $rem"
means, a dynamic attribute will be added to the HTML element when the state count
has a value, the attribute added will be style
attribute and the value for the style attribute will be
font-size: <StateValue>rem
<div id="my-component">
<div>
Count:
<span data-bind="count">
<!-- This placeholder will be updated with value of count state -->
</span>
</div>
<div>Show plus: <span data-bind="showPlus">x</span></div>
<div>Hide minus: <span data-bind="hideMinus">x</span></div>
<div class="buttons">
<!-- Increment button will be visible if showPlus state is set to true -->
<button id="increment" data-bind-visible="showPlus">+</button>
<button id="decrement" data-bind-hidden="hideMinus">-</button>
<!-- Bind attribute, state :: attr :: value, $ for stateValue placeholder -->
<button id="increment" data-bind-attr="disablePlus::disabled::">+</button>
<div>
Count:<span
data-bind-attr="count::style::font-size: $rem"
data-bind="count"
>x</span
>
</div>
<div>
NumberStatus:
<span data-bind-visible="numberStatus::medium">Medium</span>
<span data-bind-visible="numberStatus::max">Max</span>
<span data-bind-visible="numberStatus::min">Min</span>
</div>
</div>
</div>
import Comfey from 'comfey';
const app = new Comfey(document.getElementById('app'));
// Select buttons
const btnIncrement = document.getElementById('increment');
const btnDecrement = document.getElementById('decrement');
// Initialize states
const [, setShowPlus] = app.useState('showPlus', true);
const [, setHideMinus] = app.useState('hideMinus', false);
const [count, setCount] = app.useState('count', 3, countWatcher);
function countWatcher(newVal) {
if (newVal > 4) {
setShowPlus(false);
} else {
setShowPlus(true);
if (newVal < 1) {
setHideMinus(true);
} else {
setHideMinus(false);
}
}
}
// Button behaviours
btnIncrement.addEventListener('click', () => {
setCount(count() + 1);
});
btnDecrement.addEventListener('click', () => {
setCount(count() - 1);
});