npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

defi

v1.3.8

Published

Data binding without framework

Downloads

147

Readme

If you're looking for defi-react get its README here.

It can be installed via NPM:

npm i defi
const { bindNode, calc } = require('defi');

bindNode(obj, 'key', node)

Or downloaded to use as a global variable

// use defi as a global variable
defi.bindNode(obj, 'key', node)

How would I use it?

Skip this section if you're using defi-react because React handles DOM rendering by its own.

As a simple task let's say you want to define a simple form with first name and last name input, where while you type a greeting appears.

<input class="first">
<input class="last">
<output class="greeting"></output>
// default data
const obj = {
  first: 'John',
  last: 'Doe'
};

// let's listen for first and last name changes
defi.on(obj, 'change:first', () => console.log('First name is changed'));
defi.on(obj, 'change:last', () => console.log('Last name is changed'));

// we would like to re-calculate 'greeting' property every time
// when the first or last are changed
defi.calc(obj, 'greeting', ['first', 'last'], (first, last) => `Hello, ${first} ${last}`);

// and we want to set up a two-way data binding between the props
// and corresponding DOM nodes
defi.bindNode(obj, {
  first: '.first',
  last: '.last',
  greeting: '.greeting'
});

If first or last is changed then event handlers print info about that to console, greeting property is updated, .greeting element is populated by calculated data (by default "Hello, John Doe"). And it happens every time when these properties are changed and it doesn't matter which way. You can do obj.first = 'Jane' or you can type text into its field, and everything will happen immediately.

That's the real accessor-based reactiveness! Check the example above here and try to type obj.first = 'Jane' at the "Console" tab.

Note that if you want to use a custom HTML element (at the example above we use <output> tag) to update its innerHTML you will need to pass so-called "binder" as a rule of how the bound element should behave. By default defi.bindNode doesn't know how to interact with non-form elements.

const htmlBinder = {
  setValue: (value, binding) => binding.node.innerHTML = value,
};
// this will update innerHTML for any element when obj.greeting is changed
defi.bindNode(obj, 'greeting', '.greeting', htmlBinder)

Also you can use html from common-binders (a collection of binders of general purpose).

const { html } = require('common-binders');
defi.bindNode(obj, 'greeting', '.greeting', html())

Note that there is also a routing library for defi.js - defi-router.

Quick API ref

Full reference with all variations, flags and tutorials live at defi.js.org.

  • bindNode - Binds a property of an object to HTML node, implementing two-way data binding.
// basic use (for standard HTML5 elements)
defi.bindNode(obj, 'myKey', '.my-element');

// custom use (for any custom element)
defi.bindNode(obj, 'myKey', '.my-element', {
    // when is element state changed?
    // (that's a DOM event; a function can be used to listen to any non-DOM events)
    on: 'click',
    // how to extract element state?
    getValue: ({ node }) => someLibraryGetValue(node),
    // how to set element state?
    setValue: (v, { node }) => someLibrarySetValue(node, v),
    // how to initialize the widget?
    // it can be initialized in any way,
    // but 'initialize' function provides some syntactic sugar
    initialize: ({ node }) => someLibraryInit(node),
});

obj.myKey = 'some value'; // updates the element
  • calc - Creates a dependency of one property value on values of other properties (including other objects).
defi.calc(obj, 'a', ['b', 'c'], (b, c) => b + c);
obj.b = 1;
obj.c = 2;
console.log(obj.a); // 3
  • mediate - Transforms property value on its changing.
defi.mediate(obj, 'x', value => String(value));

obj.x = 1;

console.log(obj.x); // "1"
console.log(typeof obj.x); // "string"
  • on - Adds an event handler. Detailed information about all possible events you can get at the "Events" section of the website.
defi.on(obj, 'change:x', () => {
	alert(`obj.x now equals ${obj.x}`);
});

obj.x = 1;
  • off - Deletes an event handler.
defi.off(obj, ['change:x', 'bind']);
defi.on(obj, ['foo', 'bar'], (a, b, c) => {
	alert(a + b + c);
});
defi.trigger(obj, 'foo', 1, 2, 3); // alerts 6
defi.bindNode(obj, 'myKey', '.my-element');

defi.unbindNode(obj, 'myKey', '.my-element');
  • bound - Returns a bound element.
defi.bindNode(obj, 'myKey', '.my-element');
const node = defi.bound(obj, 'myKey'); // will return document.querySelector('.my-element')
  • chain - Allows chained calls of defi.js functions.
defi.chain(obj)
    .calc('a', 'b', b => b * 2)
    .set('b', 3)
    .bindNode('c', '.node');
  • defaultBinders - An array of functions which return a corresponding binder or a falsy value. This makes bindNode to detect how to bind a node if the binder argument isn't given.
defi.defaultBinders.unshift(element => {
	// check if the element has "foo" class name
	if(element.classList.contains('foo')) {
		// if checking is OK, return a new binder
		return {
			on: ...,
			getValue: ...,
			setValue: ...
		};
	}
});

// ...

defi.bindNode(obj, 'myKey', '.foo.bar');
const element = document.createElement('input');
element.type = 'text';

console.log(defi.lookForBinder(element));
  • remove - Deletes a property and removes attached change handlers.
defi.remove(obj, 'myKey');
  • set - Sets a property value allowing to pass an event options object.
defi.set(obj, 'myKey', 3, { silent: true });