Document listener
This commit is contained in:
parent
5e19b2d70f
commit
ca600401ed
3 changed files with 163 additions and 44 deletions
19
index.html
19
index.html
|
@ -55,6 +55,25 @@
|
|||
<a class="button" href="page/element.html">Read More</a>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Listener</h2>
|
||||
<p>
|
||||
Like a normal object, except you can register callbacks for property changes.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<h3 class="all-unset"><b>Code Sample</b>:</h3>
|
||||
<code-block>
|
||||
import listener from 'listener.js'
|
||||
|
||||
const user = listener({name: "John Doe"})
|
||||
user.listen('name', value => console.warn(`User name has changed to ${value}`))
|
||||
</code-block>
|
||||
</p>
|
||||
|
||||
<a class="button" href="page/listener.html">Read More</a>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Debounce</h2>
|
||||
<p>
|
||||
|
|
63
listener.md
63
listener.md
|
@ -5,53 +5,28 @@ A generator for proxy objects that run callbacks when properties are changed.
|
|||
## Interface
|
||||
|
||||
```js
|
||||
listener(target={})
|
||||
// Creates a new proxy for target
|
||||
listener()
|
||||
// Creates a new listener object
|
||||
listener(some_object)
|
||||
// Creates a new listener object that acts as a proxy for some_object
|
||||
listener.listen(prop, callback)
|
||||
// Adds a callback on a property change
|
||||
listener.listen([prop, ...], callback)
|
||||
// Adds a callback to several properties at once
|
||||
listener.listen(prop)
|
||||
// Removes all callbacks from a given property
|
||||
// Adds a callback on a specific property change
|
||||
listener.listen(null, callback)
|
||||
// Adds a callback on any property change
|
||||
listener.listen(prop, callback, {once: true})
|
||||
// Adds a one-time callback
|
||||
```
|
||||
|
||||
```js
|
||||
text(listener, "property")
|
||||
// Returns a text node bound to listener.property
|
||||
text(listener)
|
||||
// Returns a text node proxy for the listener
|
||||
text(listener).property
|
||||
// Same as first example
|
||||
listener.forget(prop, callback)
|
||||
// Removes a specific callback on a specific property
|
||||
listener.forget(prop, null)
|
||||
// Removes all callbacks from a property
|
||||
// Note that undefined won't work, to avoid accidents
|
||||
// it really has to be null
|
||||
listener.forget(null, callback)
|
||||
// This is not a special case, it simply removes a
|
||||
// callback that was registered with lisetner.listen(null, callback)
|
||||
```
|
||||
|
||||
When called with two arguments, this function returns a new text node that will
|
||||
automatically update to reflect the given property on the listener.
|
||||
|
||||
When called with only the listener, it creates a proxy object that, when
|
||||
indexed, returns the result of calling `text` on the listener and the indexed
|
||||
property name.
|
||||
|
||||
Note that repeatedly indexing the proxy will return a new text node each time.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import Listener from 'listener.js'
|
||||
const listener = Listener({})
|
||||
|
||||
// Listen for any changed property
|
||||
listener.listen("*", (value, prop) => console.log(`${prop} changed to ${value}`))
|
||||
|
||||
// Listen only for changes to the foo property
|
||||
listener.listen("foo", prop => console.log("it was foo, by the way"))
|
||||
|
||||
// Several listeners for one property are possible
|
||||
// They will be executed in order of definition
|
||||
listener.listen("foo", prop => do_something())
|
||||
|
||||
listener.foo = "New Value"
|
||||
// Triggers 3 handlers
|
||||
|
||||
listener.bar = "New Value"
|
||||
// Triggers only the * handler
|
||||
```
|
||||
Note: Forgetting one-time callbacks is not (yet) possible.
|
||||
|
|
125
page/listener.html
Normal file
125
page/listener.html
Normal file
|
@ -0,0 +1,125 @@
|
|||
<link rel="stylesheet" href="style.css">
|
||||
|
||||
<script type="module" src="codeblock.js"></script>
|
||||
<script type="module" src="filesize.js"></script>
|
||||
|
||||
<h1 class="js module">listener.js</h1>
|
||||
|
||||
<code-block>import listener from 'listener.js'</code-block>
|
||||
|
||||
<section>
|
||||
<h2>Description</h2>
|
||||
<p>
|
||||
Listeners are special proxy objects that can trigger (one or several) callbacks when any of its properties are set.
|
||||
</p>
|
||||
<dl>
|
||||
<code>
|
||||
<dt>listener</dt>
|
||||
<dd>target ⟶ proxy</dd>
|
||||
<dd><i>nil</i> ⟶ proxy</dd>
|
||||
</code>
|
||||
<dd>
|
||||
A factory function for new listener objects.
|
||||
When an object is passed as an argument, the listener will act as a proxy for that object.
|
||||
Otherwise a new target object is created.
|
||||
</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Example</h2>
|
||||
|
||||
<code-block>
|
||||
import MyUserComponent from '/path/to/MyUserComponent.js'
|
||||
// Suppose this component allows the user to enter a new name
|
||||
import backend from '/path/to/backendController.js'
|
||||
// Suppose this class abstracts some backend API
|
||||
|
||||
const user = listener({name: "World"})
|
||||
// Greet the user whenever the name changes:
|
||||
user.listen("name", newName => alert(`Hello, ${newName}!`))
|
||||
// Save the in the back-end whenever it changes:
|
||||
user.listen(null, (value, old) => { if (value != old) backend.saveUserSomehow(user) }))
|
||||
|
||||
document.body.apend(new MyUserComponent(user))
|
||||
</code-block>
|
||||
|
||||
<p>
|
||||
This code uses a listener proxy to represent a User so it can respond whenever the user gets updated.
|
||||
</p>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Methods</h2>
|
||||
<p>
|
||||
The proxy object will override the following two properties of the target object:
|
||||
<dl>
|
||||
<dt><code>listener.listen(<prop>, <callback>)</code></dt>
|
||||
<dd>Registers a callback on a single property that will be called whenever the property changes.</dd>
|
||||
<dt><code>listener.listen(null, <callback>)</code></dt>
|
||||
<dd>Registers a callback that will be called whenever <em>any</em> property changes.</dd>
|
||||
<dt><code>listener.listen(<prop>|null, <callback>, {once: true})</code></dt>
|
||||
<dd>Registers a callback that will only be called once, then remove itself.</dd>
|
||||
<dt><code>listener.forget(<prop>, <callback>)</code></dt>
|
||||
<dd>Forgets a specific callback for a specific property.</dd>
|
||||
<dt><code>listener.forget(<prop>)</code></dt>
|
||||
<dd>Forgets all callbacks for a certain property.</dd>
|
||||
<dt><code>listener.forget(null[, <callback>])</code></dt>
|
||||
<dd>
|
||||
Forgets one or all generic callbacks.
|
||||
This will <em>not</em> delete any specific property callbacks.
|
||||
In other words, this is not a mechanism for clearing all callbacks of a listener.
|
||||
</dd>
|
||||
</dl>
|
||||
</p>
|
||||
|
||||
<dl>
|
||||
<code>
|
||||
<dt>callback</dt>
|
||||
<dd>new, old, prop ⟶ <i>nil</i></dd>
|
||||
<dt>prop</dt>
|
||||
<dd>string</dd>
|
||||
</code>
|
||||
</dl>
|
||||
|
||||
<p>
|
||||
<strong>Note</strong> that callbacks will run regardless of whether the new and old values are actually different.
|
||||
It is up to the user to compare the new value to the old one and possibly only take action when there is a meaningful difference.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Callbacks are internally stored as a map from property name to a set of callbacks.
|
||||
Therefore, registering the same callback on the same property more than once will have no effect;
|
||||
the callback will only be called once for every property change.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Note</strong> that one-time callbacks registered with <code>{once: true}</code> will generate
|
||||
a new wrapper function that removes itself before calling the actual callback.
|
||||
This has two implications:
|
||||
<ul>
|
||||
<li> One-time callbacks cannot be forgotten by passing the original function, as that is not the callback that is saved internally
|
||||
<li> Adding the same one-time callback repeatedly will generate a new (unique) wrapper every time, so they will also run more than once
|
||||
</ul>
|
||||
This behaviour of one-time callbacks <em>may</em> change in the future and should <em>not</em> be relied on.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Additionally, the <code>listener</code> factory itself has the following method<!-- Change to plural when adding more! -->:
|
||||
<dl>
|
||||
<code>
|
||||
<dt>listener.raw</dt>
|
||||
<dd>proxy → target</dd>
|
||||
</code>
|
||||
<dd>
|
||||
This static method can be used to retrieve the target object for a given listener proxy.
|
||||
</dd>
|
||||
</dl>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Note</strong> that callbacks will only trigger when the property is set via the proxy object.
|
||||
Changing the raw object directly will not trigger any callbacks as javascript provides
|
||||
no mechanism to detect such property changes on plain objects.
|
||||
</p>
|
||||
</section>
|
Loading…
Reference in a new issue