Signy from scratch
Build a shopping list, one idea at a time. No framework experience needed.
This page is for you if you know some HTML and a little JavaScript and have never used a UI library. You will build a small shopping list. Each step adds one idea, and every example on the page is complete: copy it into your file and it works.
1. What a UI library does for you
A page often has to change when data changes: a counter goes up, an item appears in a list. In plain JavaScript you find the element and change it yourself, every time.
That works for one number. With ten numbers shown in ten places, you have to remember to update each of them, and it is easy to miss one. Signy turns that around: you change the data, and the page follows by itself.
2. One HTML file
You need no installation and no build tool. Create a file called
shopping.html, paste this in, and open it in your browser.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Shopping list</title>
<script src="https://cdn.jsdelivr.net/npm/signy@1/dist/signy.global.min.js"></script>
</head>
<body>
<div id="app"></div>
<script>
// The code of each step goes here
</script>
</body>
</html>
The first script tag loads Signy and gives you a global object called
Signy. Every example below starts by taking what it needs
from it, such as const { Signal, html } = Signy;. In your
file, replace the id in the last line of each example with
"#app".
3. A signal is a box with a value
A signal holds one value and remembers who reads it.
You read it with .value and change it by giving
.value a new value. Here is the apple counter again.
There is no line that updates the text. The text reads the signal, so Signy updates it whenever the signal changes. That is the whole idea.
${() => this.apples.value},
with () => in front. That little arrow makes a function,
and Signy calls it again when the value changes. Without it,
${this.apples.value} puts the value in once and it never
changes again.
4. A component is a piece of the page
The example above is a component: a JavaScript class that describes one piece of the page. If you have not used classes:
class Apples extends Component { ... }creates a new kind of component, based on the one Signy provides.apples = new Signal(0);is a field: a value each component carries. Inside the class you reach it asthis.apples.render()returns the HTML of the component. Signy calls it once, when the component appears. It never calls it again: the functions inside keep the page up to date.application(Apples).mount("#app")puts the component into the element with the idapp.
The HTML is written between backticks after html. That is
a template. The ${...} parts put
JavaScript values into it.
5. Reacting to clicks and typing
onClick=${...} runs a function when the element is clicked,
and onInput=${...} runs one each time the user types. The
function gets an event, and
event.currentTarget.value is the text in the field.
Notice <input /> ends with />. In a
template, elements that have no closing tag, such as
input, img and br, need that
slash.
6. Showing and hiding
To show one thing or another, use Show. It takes a
when function, what to show when it is true
(children) and what to show otherwise
(fallback).
7. Lists
A signal can hold a list. To change it, give the signal a
new list. Changing the old list in place, with
push(), does not tell anyone. For shows one
row per item and updates only the rows that changed.
[...this.items.value, newItem]makes a new list with everything from the old one plus the new item.filter()makes a new list without the removed item.- Each item has an
id, andkeyBy: "id"tellsForto use it. That wayForknows which row is which, even after you add or remove items.
8. Values computed from other values
The number of items comes from the list. Instead of keeping it in a
second signal, compute it with Computed. It updates when
the list does.
9. Splitting into components
When a component grows, move a part into its own component and use it
like a tag: <${Total} />. Values you pass on the tag
are its props, and it reads them as
this.props.
A prop is read once, like everything in render(). For a
value that changes, pass a function and call it inside
() =>. What you put between the opening and closing tag
arrives as this.props.children.
10. When something does not work
| What you see | Why | Fix |
|---|---|---|
| The number on the page never changes | The value is put in once | Write ${() => this.count.value}, not ${this.count.value} |
The list does not change after push() | The signal still holds the same list, so nobody is told | Give it a new list: this.items.value = [...this.items.value, item] |
A list made with .map() never changes | .map() runs once, when the page is built | Use For |
| The input keeps its text after you clear the signal | The field's text belongs to the browser once someone typed | Reset the form with form.reset(), or set the field through a ref |
| The page is empty, or half of it is missing | An <input> or <br> without the slash | Write <input /> and <br /> |
| Nothing shows at all | An error in your code | Open the browser console (F12) and read the first red message |
11. Where to go next
- How Signy works explains why the rules above are what they are.
- Signals, Components and For and Index go into detail on what you used here.
- Examples show whole apps, a todo list among them, with all their code.