OtoReact is a library that brings reactivity straight into your HTML file.
The first application above is the result of this piece of Reactive HTML (RHTML):
The Reactive HTML is placed in an HTML document that loads the OtoReact library, e.g. like this:
That's all you need.
Full framework functionality, offered by a library.
Nothing to install, to configure, to build, and not a single line of required JavaScript code.
Running like a charm.
'Reactive web applications', or 'Single Page Applications', are web applications that, when possible, react immediately on user input, timer events etc, or the receival of server data requested by the application, instead of having the web server generate and send a new HTML page.
This results in both a much better user experience and much less server load.
Two tiny examples you see above; please enter some data.
Reactivity is attained by means of JavaScript programming code running inside the web browser. The JavaScript code has to manipulate the so-called "DOM" ("Document Object Model"), which is the internal object model of a web page ("document").
Writing such JavaScript by hand can get quite complicated and might result in very cluttered programming code. A framework makes it much easier to create reactive web applications.
OtoReact is a small and fast framework library to attain reactivity by loading so-called Reactive HTML, or RHTML, straight into the browser:
Let's return to the tiny example above left. You can modify the source code below and the modified application will be reloaded immediately.
Explanation:
yourName
, that can contain state information of the application.
yourName.V
denotes the value of the variable.store='sessionStorage'
, which makes the value persist when the application is reloaded.@value="yourName.V"
binds the value of the reactive variable to the value of the input element. Whenever the input element receives input, OtoReact will update the reactive variable and all document content that reacts on it.
yourName.V
is non-empty, then the body of the conditional is rendered.
\{yourName.V\}
and \{yourName.V.length\}
are embedded expressions. The rendered document will contain the value of the expression between the braces.
Explanation:
maxY
with initial value 10, to persist in sessionStorage
.
y
iterate through the values of range(1,maxY.V)
, which are the numbers 1 to maxY.V.
Here is an example of dynamically building a table based on server data, with a bit of animation as well:
There exist quite a number of alternative libraries and frameworks to make it easier to build reactive web applications. I distinguish two main categories:
Reactive HTML combines features of both approaches, giving you the advantages of describing the desired reactive layout by a HTML template, separate from your programming code, but without the hassle of installing and using a compiler and managing quite a number of configuration files, and you need just a basic understanding of JavaScript.
This document for example changes its layout when being printed or viewed on a narrow screen: the table of contents moves to the top, and on a mobile device font sizes are adapted.
Responsiveness is usually attained by using CSS media queries, which is perfectly possible in Reactive HTML.
When you get stuck with media queries, then reactivity may come to the rescue, to make responsive adaptations to your document or style definitions not possible with media queries.
Using ".rhtml" might also cause confusion with Ruby on Rails HTML files, that are to be processed by the server-side Ruby on Rails engine.
<{}script type=module src="path/to/OtoReact.js"><{}/script>
rhtml
:
<{}body rhtml>, then OtoReact will implicitly compile and render the document body at the first event cycle.
rhtml
attribute<{}body rhtml hidden>
.
That's all!
1 + 1 = \{1 + 1}
, which results in the text "1 + 1 = {1 + 1}".
The expressions are evaluated, converted to string, and inserted as text; there is absolutely no risk of code injection.
To include a normal pair of braces in RHTML, at least one of them must be escaped with a backslash.
Within JavaScript, you can of course use the JavaScript syntax for template literals, using backquotes and dollar signs:
let x = `Some text ${expression} et cetera`;
If you prefer, you may add a dollar sign in RHTML as well, like 1 + 1 = $\{1 + 1}
.
null
and undefined
are not shown. This is unlike JavaScript template literals, where `$\{null}`
yields "null"
.
\{}
, which comes in handy if you want the parser to not recognize an HTML tag:
RHTML defines a number of new constructs, which dynamically build your HTML page:
reacton
does the same thing.
RCompile(HTMLElement, options?)
compiles and builds the given HTMLElement as RHTML, using the given options.
RVAR(name?, initialValue?, store?)
creates a reactive variable.
reroute()
and docLocation
are used with URL routing.
range(start?, count, step?)
yields an iterable range of count numerical values: start, start+step, …, start+(count-1) * step.
RFetch(resource, init?)
is the same as fetch(resource, init?)
, except that it throws an error when an HTTP error status is received.
All of these are exported by the OtoReact module. TypeScript type declarations are available on the download page.
\{ … \}
of JavaScript code
RHTML adds another kind of variable: RHTML local variables; these are visible in all inline JavaScript code within a block of RHTML code.
These are introduced by the following RHTML constructs:
RHTML local variable names obey strict lexical scoping rules, see
RHTML local variables are not visible within other
Global variables are created:
globalThis
:
globalThis.varName = value;In web browsers, the global object is commonly named "
window
", but in other JavaScript environments it is named otherwise, and using "window
" causes confusion with real window properties. Hence ECMAScript 2020 introduced the new cross-platform name globalThis
to refer to the global object, and we suggest you use this rather than window
.
defines
attribute of
Reactive variables (RVAR's) are objects containing variable data on which RHTML elements may react.
Anytime the value of an RVAR changes, the reacting elements will be updated.
RVAR's are created:
RVAR(name?, initialValue?, store?, subscriber?, storename?)
from JavaScript,name
name
, the RVAR will be registered in the global environment under that name and will be visible anywhere.
<{}script type=module> const x = RVAR('X'); …, then it will be available as
x
just inside this module, and also as X
anywhere (though normally one shouldn't use different local and global names).
initialValue
initialValue
is a Promise, then the value of the RVAR will initially be undefined
, and when the promise resolves to a value, then the RVAR will be set to that value.
store
*subscriber
*x.Subscribe(subscriber)
.
storename
*
You may or must inform OtoReact which fragments of RHTML should react on which RVAR's, by using the reacton
attribute, see below.
For RVAR's created by calling RVAR()
, this is necessary.
For RVAR's created by
An RVAR x
is an object, distinct from the value of the variable. It has the following properties and methods:
x.V
x
, one writes x.V
.
x.V
is set to a different value than it had before, then the RVAR is marked as dirty, and all RHTML code that reacts on it will be updated.
x.U
x.U
to get the value of the RVAR.
E.g., if x.V
is an array, you can write x.U.push(e)
or x.U[i] = e
to add an or modify an array element, and the DOM will react on the modified array. So you don't have to assign to x.V
to trigger a reaction.
Exception: within an HTML attribute or property definition, accessing x.U
does not mark the RVAR as dirty. This is so that one can use x.U
within RHTML two-way bindings, and the RVAR will only be marked dirty when the property is modified by an event, not when it is being set by the RHTML engine.
Setting x.U
sets the value of x
and marks it as dirty even when the value is equal to the previous value.
x.SetDirty()
x.SetDirty()
.
x.Subscribe(subs, bImmediate?, bInit?)
*subs
, when not null, is registered as a subscriber to x
, so it will be called whenever x
has been set dirty.
bImmediate
is truthy, subs
will be called immediately every time x
is being set dirty; otherwise it will be called once at the next event cycle.
bInit
is truthy, then subs
will initially be called when it is registered. The default value for bInit
is the value of bImmediate
.
The return value is x
, so calls to Subscribe
can be chained.
x.UnSubscribe(subs)
*subs
previously registered as a subscriber to x
.
x.Set(value)
*x.V
either synchronously, or asynchronously when value
is a Promise.
When it is a Promise, then x.V
will initially be undefined
, and when the promise resolves to a value, then x.V
will be set to that value.
x.Set
*x
, i.e. v => x.Set(v)
.
This is handy to create an errorhandler. E.g., if errMsg
is an RVAR that should receive error messages, then you can write doSomething().catch(errMsg.Set)
to catch the errors of some asynchronous routine, or you can add an attribute #onerror="errMsg.Set"
to catch all errors within a block of RHTML.
x.Clear
*x
, except when x
has just been set dirty in the same event loop.
You can e.g. add an attribute #onsuccess="errMsg.Clear"
to clear any error message when any event handler succeeds without error.
store
parameter to RVAR()
or to store
can be:
sessionStorage
, meaning that the value will be restored when the user reloads the page, or leaves it and returns again while staying in the same browser windowlocalStorage
, meaning that the value will be preserved in local browser storage and restored when the user returns to the same site in the same browser on the same machinesetItem
and getItem
methods of the Storage interface.
The RVAR must have a unique storename
; the default is `RVAR_$\{name\}`
, where the prefix "RVAR_" can be changed using option 'store_Prefix'.
An example using sessionStorage
can be found in the Multiplication demo: modify the numbers; then either reload the page or modify the source code, which triggers a reload too, and see that the modified numbers have persisted.
Scripts can be included anywhere using the
Depending on the script type, they are either executed just once, or every time the surrounding element is being instantiated (built).
Scripts in OtoReact can export variables, so that these variables are either globally defined or locally visible in RHTML code.
See <{}SCRIPT> for details about
Styles sheets have global effect, except inside an 'isolated' component or inside
There is no provision yet for style sheets with local scope. To use local styling, just add local class names to each styling rule.
Style sheets inserted with
To build up dynamic style sheets using string interpolation and other OtoReact features, you can use
RCompile
does not, as one might perhaps expect, translate the whole chunk of RHTML into one large string of JavaScript.
eval
function (in global scope, of course).
a+b
is compiled by calling eval("([a,b]) => (a+b)")
.
OtoReact does not itself parse and analyse the JavaScript, so it is unaware which variables are actually used and which are not.
setTimeout
) all reacting DOM tree parts to get updated by their registered builder routine in its registered environment.
, then the (Chromium) parser will move the`"> … \{x} …
x
being undefined.
When building the DOM tree, the dots will be removed.` "> … \{x} …
…, without closing the` "> …
…
#class:someclass="someBoolean"
), which will only work for class names in lowercase.I imagine Reactive HTML one day being natively supported by the browser. In that case, these limitations can be lifted.
// RCompile(…)
reacton
attribute.
bAbortOnError
.
debugger
statement, to get a breakpoint at that point when the browser development tool is opened.
Built with OtoReact