In my recent post about AI-UI, I touched on why I developed the library.
- I wanted declarative markup
- I wanted type-safe component encapsulation, with composition & inheritance
- I wanted a small, fast, client-side solution with no complex build process (or any build process!)
...but most importantly...
- I didn't want to learn a whole new API just to manipulate data I already have in my app
In my single page apps, I've got remote APIs and endpoints that supply data. Sure, I might want to do a bit of arithmetic, sorting, formatting, but basically, I want to say to the UI: "Here's my data, render it into the DOM". When the data changes, either because the server tells me or the user manipulates it locally, I don't want to re-render anything - I've already laid out my page - I just want the DOM to automagically re-render.
How can I do this using the most familiar JS syntax possible? Iterable Properties
Show me!
Let's start with one of the basic examples in the AI-UI repo: a clock.
We're not really interested in the clock itself, we're going to use Chrome Dev tools create iterable properties on a basic JS object, and see how they work. Go to the example, and open Dev Tools with Ctrl (Cmd) + Shift + I
We're using this script (as opposed to the ECMA module) as it defines the global constant AIUI
, so we can just play.
A key sub-module of AIUI
is the Iterators
interface. Let's grab a reference to it for later use, and just create a plain old JS object.
In the Dev Tools console, type:
defineIterableProperty = AIUI.Iterators.defineIterableProperty;
xxx = { foo: 123 };
So xxx
is just an object. Nothing special here. Let's add an iterable property to it. The syntax is defineIterableProperty(object, key, initialValue)
:
defineIterableProperty(xxx,"bar",456);
// xxx.bar just looks like a normal property:
xxx.bar * 10
// 4560
xxx.bar -= 111;
// 345
But xxx.bar
has some magic: it's also an async iterator:
for await(const v of xxx.bar) console.log("bar is now",v)
// bar is now 345
xxx.bar = 100
// bar is now 100
//100
xxx.bar++
// bar is now 101
// 100
xxx.bar = "Wow!"
// bar is now Wow!
//'Wow!'
If you prefer a more functional style, AI-UI comes with a handy bunch of helpers like map
, filter
and consume
xxx.bar.map(v => v * 10).consume(v => console.log("Or 10x more is",v));
// Promise {<pending>}
xxx.bar = 99;
// bar is now 99 <--- for...await is still going!
// Or 10x more is 990 <--- same value mapped
// 99
Is that it?
Yep. That's it. No 'setState()', no hooks, signals, or other API to learn. You just define an iterable property, read and write to it like normal, and consume the results as it changes.
It's this simplicity that makes AI-UI one of the simplest UI modules to use. Just layout your markup specifying iterable values in your variable substitutions like <div>${xxx.bar}</div>
, and it just works by consuming your iterable values and updating the DOM.
Of course, defining all your own properties by hand is a bit repetitive, so AI-UI components can create a bunch for you using the iterable
member, but that's not the core functionality.
The Iterators
submodule is pure-JS, with no DOM dependencies, so you can use it in NodeJS apps with
import '@matatbread/ai-ui/esm/iterators.js';
Start iterating now!
Top comments (0)