signalizejs/signal

Reactive primitive that can be watched, used to create stores, or bound to element properties and attributes.

Installation

const {
	signal,
	Signal
} = await signalize.resolve('signal');

API

signal

Signals in a nutshell:

  • A signal in Signalize is basically a “variable” that you can watch for changes or when its value is accessed.
  • Every signal you create while using Signalize is an instance of the Signal class.
  • The method signal creates a new instance of the Signal class with the value you pass in during initialization.

Let’s start with how a signal can be used:

// 1. Creating a signal
const number = signal(0);

// 2. Setting a new value: from 0 => 1;
// You need to pass a value
number(1)

// 3. Getting signal value
// You just call the method
const actualNumber = number(); // => 1

// 4. We may want to watch the signal
// See docs below for more info

// 4.1 Triggered after every set
const unwatch = number.watch(({ newValue, oldValue }) => {});
// 4.2 Triggered immediately
number.watch(({ newValue, oldValue }) => {}, { immediate: true });
// 4.3 Triggered before the value is set
number.watch(({ newValue, oldValue }) => {
	return {
		// If false, the new value will not be set
		settable: true,
		// We can also modifie the value within the watcher
		value: newValue
	}
}, { execution: 'beforeSet'});
// 4.4 - And if we want to track the signal value usage
// we can watch it on every get call
number.watch(({ newValue, oldValue }) => {}, { execution: 'onGet' });

Signals can be watched for the following events. See the example below:

  • on every get - To track places where the signal is used.
  • before it is set -Useful to check the new value and possibly modify the value or block the setter.
  • after it is set - Trigger some callback like recalculating variables that depend on the signal.

Signals are often used with the bind method.

Below is an example with two number inputs:

  • Signals are connected to elements using the bind method.
  • Signals are watched using .watch() method.
  • When any signal changes, it updates the output under the input elements.
  • numberB cannot be lower than numberA. It’s always +1 higher than numberA.
A: <input id="numberA" type="number"><br>
B: <input id="numberB" type="number"><br>
<span id="output"></span>

<script type="importmap">{
  "imports": {
    "signalizejs": "https://cdn.jsdelivr.net/npm/signalizejs/+esm",
    "signalizejs/event": "https://cdn.jsdelivr.net/npm/signalizejs/event/+esm",
    "signalizejs/mutation-observer": "https://cdn.jsdelivr.net/npm/signalizejs/mutation-observer/+esm",
    "signalizejs/scope": "https://cdn.jsdelivr.net/npm/signalizejs/scope/+esm",
    "signalizejs/signal": "https://cdn.jsdelivr.net/npm/signalizejs/signal/+esm",
    "signalizejs/bind": "https://cdn.jsdelivr.net/npm/signalizejs/bind/+esm"
  }
}</script>

<script type="module">
    import Signalize from 'signalizejs';

    const { resolve } = new Signalize();
    const { signal, bind } = await resolve('signal', 'bind');

    // 1. Init Signals
    const numberA = signal(1);
    const numberB = signal(2);

    // 2. Bind signals to inputs
    // bind(element, attributes)
    bind(document.querySelector('#numberA'), { value: numberA });
    bind(document.querySelector('#numberB'), { value: numberB });

    // 3. Bind signal to output.
    bind(document.querySelector('#output'), {
        // 4. In case we need to pass more than just a Signal
        // into the bind function, we need to tell the function
        // which signals to watch. The array contains signals
        // to watch and the listener goes last [...signals, listener]
        html: [numberA, numberB, () => `Outpout is: ${numberA + numberB}`]
    })

    // 5. Watch numberA,
    // Execute watcher immediatelly {immediate: true }
    numberA.watch(({ newValue }) => {
        // 6. When number A is set, automatically increase
        // numberB if it is smaller than A
        if (newValue > numberB()) {
            numberB(newValue + 1);
        }
    }, { immediate: true });

    // 7. Watch number B, execute it before it is set
    numberB.watch(({ newValue, oldValue }) => {
        // 8. When number B is set, but is smaller than number A,
        // return the old value and block setting the value
        if (newValue < numberA()) {
            return {
                value: oldValue,
                settable: false
            }
        }
    }, { execution: 'beforeSet' });
</script>

Signal

Signal is a class that is used when creating a new signal with the signal method.

Because Signal is a class, it can be easily extended for additional functionality. This way, we can make a specific Signal that can be reused within the application.

Let’s make a signal that will contain increment method.

const { Signal } = await signalize.resolve('signal');

class Number extends Signal {
	increment() {
		this.set(this.get() + 1);
	}
}

const number = new Number(0);
number.increment();
console.log(number()); // => 1