The easiest way for setting up a new callbag-jsx
project is to use one of the following
starter templates on GitHub. Open the repo of your choice, and follow the instructions on the README.
π Read the installation guide for other methods of installing callbag-jsx
.
A simple Hellow World! app looks like this:
1linkimport { makeRenderer } from 'callbag-jsx';
2link
3linkconst renderer = makeRenderer();
4linkrenderer.render(<div>Hellow World!</div>).on(document.body);
π Here is what happens:
<div/>
element on document.body
.info ALWAYS name your renderer object
renderer
.
In the Hellow World! exmaple above, we created a <div/>
element inside JavaScript.
This is because we are using an extension of JavaScript called JSX, which allows us to
have JavaScript variables, expressions, etc inside our layout code seamlessly:
1linkconst name = 'Jack';
2link
3linkrenderer.render(
4link <div>Hellow {name}!</div>
5link).on(document.body);
π JSX expressions create HTML elements, so you can for example store them in variables:
1linkconst x = <div>Hellow {name}!</div>;
2linkrenderer.render(x).on(document.body);
info You MUST have a renderer named
renderer
in any scope with JSX.
Callbags are models for stuff that change. When you embed them within your JSX, the resulting DOM will also change when the embeded callbags change:
1linkimport state from 'callbag-state';
2link
3linkconst count = state(0);
4linkrenderer.render(
5link <div>You have been here {count} seconds!</div>
6link).on(document.body);
7link
8linksetInterval(() => count.set(count.get() + 1), 1000);
π This works similarly with attributes:
1link<div title={count}>Hover to see how many seconds you've been here.</div>
Capture DOM events by providing an event listener function:
1linkconst count = state(0);
2link
3linkrenderer.render(
4link <div onclick={() => count.set(count.get() + 1)}>
5link You have clicked this {count} times!
6link </div>
7link).on(document.body);
Fetch user input using _state
attribute:
1linkconst input = state('');
2link
3linkrenderer.render(<>
4link <input _state={input} type='text' placeholder='type something ...'/>
5link <div>You typed: {input}</div>
6link</>).on(document.body);
Use expr
to dynamic expressions from other callbags:
1linkimport { expr } from 'callbag-common';
2link
3linkconst input = state('');
4linkconst length = expr($ => $(input).length);
5link
6linkrenderer.render(<>
7link <input _state={input} type='text' placeholder='type something ...'/>
8link <div>You typed {length} characters.</div>
9link</>).on(document.body);
Set dynamic styles for your element by providing a style map with some of its values being callbags:
1linkconst count = state(0);
2link
3linkconst add = () => count.set(count.get() + 1);
4linkconst color = expr($ => $(count) % 2 ? 'red' : 'green');
5link
6linkrenderer.render(
7link <div onclick={add} style={{ color }}>
8link You have clicked {count} times!
9link </div>
10link).on(document.body);
π Use callbags similarly in class maps for dynamically changing classes:
1link<div class={{ odd: expr($ => $(i) % 2 === 0) }}/>
<Conditional/>
component enables creating elements that appear conditionally:
1linkimport { Conditional } from 'callbag-jsx';
2link
3linkconst show = state(true);
4link
5linkrenderer.render(<>
6link <input type='checkbox' _state={show}/> Show stuff
7link <Conditional if={show}
8link then={() => <div>π¦πͺStuff Are Shown ... πͺπ¦</div>}
9link else={() => <div>Not showing stuff</div>}
10link />
11link</>).on(document.body);
Use <List/>
component to create dynamic lists:
1linkimport { List } from 'callbag-jsx';
2link
3linkconst records = state([]);
4linkconst add = () => records.set(records.get().concat(new Date()));
5linkconst clear = () => records.set([]);
6link
7linkrenderer.render(<>
8link <button onclick={add}>Add</button>
9link <button onclick={clear}>Clear</button>
10link <ul>
11link <List of={records} each={record => <li>{record}</li>}/>
12link </ul>
13link</>).on(document.body);
Use <Wait/>
component for rendering based on data that is fetched asynchronously.
1linkimport { Wait } from 'callbag-jsx';
2link
3linkrenderer.render(
4link <Wait
5link for={fetch('https://pokeapi.co/api/v2/pokemon/charizard').then(res => res.json())}
6link with={() => <>Loading ...</>}
7link then={pokemon => <h1>{pokemon.name}</h1>}
8link />
9link).on(document.body);
In callbag-jsx
, components are functions that are used to create similar parts of the UI:
1linkconst records = state([]);
2linkconst add = () => records.set(records.get().concat(new Date()));
3linkconst clear = () => records.set([]);
4link
5linkfunction Record({ record }, renderer) {
6link const remove = () => records.set(records.get().filter(r => r !== record.get()));
7link
8link return <div>{ record } <button onclick={remove}>X</button></div>
9link}
10link
11linkrenderer.render(
12link <>
13link <button onclick={add}>Add</button>
14link <button onclick={clear}>Clear</button>
15link <List of={records} each={record => <Record record={record}/>}/>
16link </>
17link).on(document.body);
info Components MUST have a second argument called
renderer
.