For most of these, the library won't warn you if you violate them. Part of this is because the project is in alpha. It might just silently fail — sometimes on load and sometimes later, like when it's supposed to react to something.
Syntax
-
White space is ignored. These would all work:
{if x} { if x } { if x }
-
Attributes need not be quoted, or you can use single quotes or double quotes.
<p class={classes}></p> <p class='{classes}'></p> <p class="{classes}"></p>
-
Within an element, the only place for any template syntax is the value of an attibute. For example, you cannot do this:
<
{tagName}>Hi there!</{tagName}> -
You must use the variable for the whole value of the attribute, not just part of it. For example, this would not work:
<p class="important
{category}warning"></p>So in this case you would have to make a variable called, for example,
classes
, and its value would be the space-separated list of all of them:"important technical warning"
. An attribute's whole value is the smallest atom that you can manipulate. -
Inside elements you cannot have if- or each-blocks. These would not work:
<p
{if important} class="imporant"{/if}></p> <p class="{each classes as c} {c} {/each}"></p> -
Do not weave the template blocks and elements. An element must be wholly inside or outside a block. This would not work:
{if odd} <li class="odd"> {else} <li class="even"> {/if}
some stuff </li>You must do something like this:
{if odd} <li class="odd">some stuff</li> {else} <li class="even">some stuff</li> {/if}
Of course in a case as simple as that, you might just do:
<li class="{classes}">some stuff</li>
-
The arguments can be only a bare variables, like
{var}
. You can’t have expressions, like{var.toDateString()}
or{if (x > 1)}
. There is not even a not operator, like{if ! var}
. This means you must fully prepare your data beforehand. -
The each loops do not give you access to the index.
There is no syntax like
{each array as index, item}
. All you have is the variable that holds the currently iterated item. (That item could be an object, though, with index as one of its keys, which you somehow have set beforehand.) -
Do not use the built-in property of Arrays called length, like
You have {items.length} items in your basket
. You must make your own property to hold the number of items in the array, like{total}
. It should be beside the array, not a property of it.{ fruits: [ "apple'" "orange", "banana" ], total: 3 }
Each of these obstacles can be overcome.
- In the database, you can write more complex SQL to format the data before you fetch it. You can save these complex statements in your database with an SQL feature serendipitously called views.
- Between fetching your data and giving it to the template, you can run it through some JavaScript of your own to reformat, augment, or otherwise prepare it for presentation.
- To keep dependent properties in sync, you can write the event handlers to update them all together.
-
In the data object, you can define getters, through JavaScript’s Object.defineProperty and Object.defineProperties.
Object.defineProperty(o, 'total', { enumerable: true, get() { return this.fruits.length; } });
You must make it enumerable, which is not the default.
Data
- The type of the argument to an if block must already be Boolean. It should not be some other type that you hope will be coerced to true or false when the time comes — like the number 0, the empty string, or an empty array (which, by the way, in JavaScript is still true).
- The type of the argument to an each block must be an Array, not just any Iterable.
-
Put the data in an object in the global scope, with the key word var, not const or let.
<script> var context = { name: 'Bob' }; </script>
You could also define it as a property on the object called window or globalThis. The outcome is the same.
The data variable must be global but need not be top level. It could be a property descended from a global object.
<script> var data = { stuff: { record: { name: 'Bob' } } }; </script> <script type="text/x-cob" data-key="data.stuff.record"> <p>Hi, my name is {name}.</p> </script>
- You can have more than one template on the page. They can each have their own data objects, or they can share the same one. Changes to the data object will immediately update whatever is needed in all dependent templates.
-
If you need to overwrite the entire data variable (like after refetching the data from the database after an update), then reset each template that depends on it, by setting its
data-key
to the empty string, then back to the name of the variable.data = fetchFromDatabase(); template.dataset.key = ''; template.dataset.key = 'data';
Because of the nature of the Proxy, it won't notice if the whole variable is replaced. The library would be watching a detached Proxy object, while you would be updating a new, plain object. Resetting the data-key makes the library reproxy the variable.
You can avoid this ceremony by replacing a property instead of the entire object:
data.record = fetchFromDatabase();
Event Handlers
This library uses inline HTML event handlers, even though they're out of fashion and in some places condemned. The library will augment them, to make them easier to use.
- The functions run in strict mode. You're welcome.
-
The functions are async, so you can use await.
<button onclick="x = await fetchFromAfar()">Get</button>
-
Each event handler has access to two variables. this refers to the element, and event refers to the event object. These have been provided natively by the browser since time immemorial.
Other than this and event, an inline handler normally can see only global variables. But with this library, the properties of the data object can be used without qualification.
<script> var data = { clicks: 0 }; </script> <script type="text/x-cob" data-key="data"> <button onclick="clicks++">Hit me!</button> </script>
Even the temporary variable for an item in a loop is available:
{each fruits as f} <li><input value="{f}" onchange="f = this.value"> {f}</li> {/each}
-
To add an item to an array, use
Array.push()
.<ul> {each fruits as f} <li>{f}</li> {/each} </ul> <form onsubmit=" event.preventDefault(); fruits.push(this.elements.fruit.value); this.reset(); "> <input name=fruit> <input type=submit value=Add> </form>
-
To delete an item from an array, set its variable to undefined. Don't use
Array.splice()
.<ul> {each fruits as f} <li>{f} <button onclick="f = undefined">Remove</button></li> {/each} </ul>
You might also need some code to save the change to the database:
<ul> {each fruits as f} <li> {f} <button onclick=" removeFromDatabase(f); // AJAX to update database f = undefined; // update view ">Remove</button> </li> {/each} </ul>