Templates for rp


Templates for rp. WORK IN PROGRESS!

They initially render to HTML and than update to DOM when anything changed.


rp-widget template.rpw

Use with some CommonJs runtime:

// choose a runtime before requiring templates 
var rpWidget = require("rp-widget/runtime/html5");
// require the compiled template 
var template = require("./template");
// The rp library 
var rp = require("rp");
var data = rp.variable([...]);
var param1 = rp.variable([...]);
var param2 = rp.const([...]);
rp.atomic(function() {
    // Just call the template... It'll return a HTML string 
    document.body.innerHTML = template(data /* main argument */, {
        // additional arguments 
        param1: param1,
        param2: param2,
        param3: "Teststring" // any argument is converted to a RP 
    // At the end of rp.atomic all HTML generated by templates 
    //  must be part of the DOM! 

Just write normal HTML with some extra stuff.

{{= data.a.b.c }} Renders a text
{{{= data["xyz"].b }}} Renders a html (unsafe!!)
{{ data("p")() + data("v")() }} Renders a computed value as text
{{{ data("p")() + data("v")() }}} Renders a computed value as html (unsafe!!)
As attributes
<input value="Text" disabled=true /> Const as normal
<input checked=data.a.b.c /> Bind references
<span style={{ "background: " + color() }}> Bind computed values </span>
<span checked={{{ which() ? color : data("color") }}}> Bind delegated values </span>
<div class="one" class="two"> Multiple values are joined with space </div>
<#arg data default> Give the main argument a name
<#arg param1> Additional arguments (required, will throw if missing)
<#arg param3 default=null> Additional arguments with default value (optional)
<#var items={{ [1,2,3] }}> Define a local variable (rp.variable) with initial value
<#var bool=true>
<#let temp={{{ items.size() }}}> Store temporary RP with <#let>
<#let temp2= items filter {{ _%2==0 }} reduce {{ a+b }} plus 1> With operations

A partial is used like a HTML element but starting with a upper case letter.

They get a main argument plus multiple arguments as attributes.

The content is the main partial given the the called partial. Other partials can given by <#name> and can take an argument.

You can name the main argument given to your provided partial with "as" plus identifier

<Partial "main argument" param1=additional.argument param2=123.456>
    The Partials main partial
    In first additional partial
<#abc "main argument to partial">
    In second additional partial

Example: The included "If" and "ForEach" partials:

    <ForEach items as item>
            <If item.done>
                <span>Item {{= item.text }} is done.</span>
            <#elseif {{ item("text")().length > 0 }}>
                <span>{{= item.text }} need to be done.</span>
                <span style="background: red">Empty item</span>

Because some stuff is reactive it need to be wrapped in a html element.

Not allowed if x is not constant:
    <If x>abc</If>
    <span><If x>abc</If></span>

The wrapping element (<span>) gets a special class to identify the element and exchange the html content if x is changed.

The main partial can be used a "Partial": <Partial />
<#partial as Name> Give the main partial another name (instead of Partial)
<#partial type as Name> Get the additional partial <#type> (throws if missing or multiple)
And use it: <Name />
<#partial type as Name optional> (doesn't throw if missing, but is null)
<#partial type as Name default=OtherName> Defaults to another partial
<#partial type as Name with value> Get the <#type> partial and the value (throws if missing or multiple)
<#partials type as array>
<#partials type as array>  Get all matching partials as array of {p: Partial, v: RP} (RP)
<ForEach array as element>
    <#let value = element.v delegated>
    <#let partial = element.p>
    <*{{=partial}} value />

Attributes of HTML elements can have a prefix, which describe the way they are handled.

<input attr:value=xyz />
Bind xyz to the attribute "value"
Changed of "value" are not propagated to xyz
<input property:indeterminate=xyz />
The "property" prefix bind xyz to the property
instead of the attribute
<input bpc:value=xyz />
The "bpc" prefix bind xyz to the attribute value
and the property value to xyz (bpc = on change)
bpc: "Bind Property Change"
bpku: "Bind Property Key Up"
bpkd: "Bind Property Key Down"
bpkp: "Bind Property Key Press"
bpi: "Bind Property Input"
<button on:click=someFn />
The "on" prefix just bind a event.
The argument should be a RP resolving to a function
The "on" prefix allow some special syntax for inlined functions:
<button on:click={{{ xyz.increment() }}} />
<div style:height="50px" class:my-class=true class:other-class=x></div>
Shortcuts for style and class

There are some useful defaults if you don't set the prefix. I. e. "value" defaults to "bpc:value".

Use the id attribute on html elements. They are bound to variables. You can access them in your code.


<button id="myButton" on:click={{{
    myButton === this; // Depends on runtime...
    myButton.disabled = true;

But better do it this way:

<#var buttonDisabled=false>
<button disabled=buttonDisabled on:click={{{

Custom code...

<script> __buf.push("<!!>"); </script>
Run when generating html
<script load></script>
Run at end of atomic. The HTML is now in the DOM.
<script changed data.a.b.c></script>
Handle changed (or updated, added, removed) event of RPs.
<script private myFunction(a, b)></script>
Define a local function, which can be used in other scripts or handler
<script public myFunction(a, b)></script>
Define a exported function, which can be used from outside too.
You need to instanciate the widgets with a "id" property.
<Xyz id="abc" /> <script> abc.myFunction(1,2); </script>
<script dispose>clearTimeout(t)</script>
Run when widget is no longer needed.

With webpack you don't need to compile your template. It will do it for you. Just configure the loader and the extension with:

module.exports = {
    module: {
        loaders: [
            { test: /\.rpw$/, loader: "rp-widget" }
    resolve: {
        extensions: ["", ".webpack.js", ".web.js", ".rpw", ".js"]

And require the template with require("./template").

It's not just a templating language, it's reactive template. The DOM will ever reflect the current value of the data. But it looks like a templating language, to atract users and be simple to use.

rp-widget is modular (parser -AST-> (optimizer) -AST-> generator). So it possible (and planed) to make a jade style parser too.

  • React can have templates inlined in javascript. rp-widget cannot do this.
  • React rerenders templates and compute the difference. rp-widget only renders and updates changed elements.
  • React and rp-widget are both declarative. React has one-way bindings, rp-widget two-way bindings.
/** @jsx React.DOM */
var HelloMessage = React.createClass({
  renderfunction() {
    return <div>{'Hello ' + this.props.name}</div>;
<#arg name>
<div>Hello {{= name }}</div>
/** @jsx React.DOM */
var TodoList = React.createClass({
  renderfunction() {
    var createItem = function(itemText) {
      return <li>{itemText}</li>;
    return <ul>{this.props.items.map(createItem)}</ul>;
var TodoApp = React.createClass({
  getInitialStatefunction() {
    return {items: [], text: ''};
  onChangefunction(e) {
    this.setState({text: e.target.value});
  handleSubmitfunction(e) {
    var nextItems = this.state.items.concat([this.state.text]);
    var nextText = '';
    this.setState({items: nextItems, text: nextText});
  renderfunction() {
    return (
        <TodoList items={this.state.items} />
        <form onSubmit={this.handleSubmit}>
          <input onChange={this.onChange} value={this.state.text} />
          <button>{'Add #' + (this.state.items.length + 1)}</button>
<#define TodoList>
    <#arg items>
        <ForEach items as item>
            <li>{{= item }}</li>
<#var items = {{ [] }}>
<#var text = "">
    <TodoList items=items />
    <form on:submit={{{
        <input value=text />
        <#let nextNum = items size plus 1>
        <button>Add #{{= nextNum }}</button>


  • More included Widgets
  • parse basic expressions in attributes <a href="xyz"+data.x>
  • remove whitespace when appropriate
  • optimize the resulting AST