Home

Awesome

RiotTS

Use Muut's Riot.js minimalistic framework from TypeScript.

Table of contents

Installation <a name="install"></a>

Install via npm:

npm install --save riot-typescript

Use as a <script> (global namespace)

In your main .html file add in the header section:

<head>
   <!-- loads riot and riot-ts -->
   <script type="text/javascript" src="node_modules/riot/riot+compiler.js"></script>
   <script type="text/javascript" src="node_modules/riot-typescript/riot-ts.globals.js"></script>
</head>   

Somewhere in your TypeScript files add the references:

/// <reference types="riot-typescript" />
/// <reference path="node_modules/riot-typescript/riot.global.d.ts" />

Use as a module

In your TypeScript files:

import * as Riot from "riot-typescript";

When mounting you will also need to reference to riot (for riot.mount):

Put a declaration somewhere:

declare module "riot/riot+compiler";

and then import riot:

import * as riot from "riot/riot+compiler";
...
riot.mount("*");

How to write elements <a name="howtowrite"></a>

Differently from pure Riot.js, in RiotTS elements are written as TypeScript classes extending the base type Riot.Element.

There are no external .tag files, HTML templates are defined as pure strings or are loaded from .html files.

In brief:

A class-element:

Writing a basic element <a name="basic"></a>

In your element TypeScript code (e.g. elements/my-element.ts):

@Riot.template("my-element.html")
class MyElement extends Riot.Element
{
   // ...
}

In elements/my-element.html:

<my-element>
   <div>
      This is a my custom element
   </div>
</my-element>

In your main application file:

riot.mount('*');

The @template decorator <a name="template"></a>

@Riot.template(param)

Sets the template for the element. The template parameter can be either:

Example of an element <my-hello>:

@Riot.template("<my-hello><div>hello</div></my-hello>")
class MyHello extends Riot.Element
{
}

or

@Riot.template("elements/my-hello.html")
class MyHello extends Riot.Element
{
}
<my-hello>
   <div>hello</div>
</my-hello>

External tag files are loaded via HTTP requests, which can slow down the startup of very large applications. To avoid this, tags can be precompiled and concatenated into a single javascript file to be loaded more quickly. See how to setup a grunt task that does this.

Precompiled files can also be set to index tags by their tag names rather than their path, making it possible to use a shorter syntax:

@Riot.template("my-hello")
// instead of @template("elements/my-hello.html")

Lifecycle events shortcuts <a name="lifecycle"></a>

Lifecycle events can be listened via this.on() or by defining the following methods in the class:

Note: names ending in "-ed" have been choosen to not clash with already methods on the Riot.Element interface.

Example:

@Riot.template("<myel><span></span></myel>")
class MyEl extends Riot.Element {
   constructor(opts) {      
      super();
      
      // first way to listen to unmount event
      this.on("unmount",()=>
      {         
      });
   }
      
   // alternated shorter way 
   unmounted()   
   {               
   }
}

How to create elements programmatically <a name="creating"></a>

To create an element to be attached to the DOM use the creaeElement(opts) method:

var root = querySelector('#put_here');
el = test1.createElement();                
root.appendChild(el);         

The created element is an instance of HTMLElement. To access the related riot-element:

var riotel = (el as any)._tag;  // ._tag is undocumented

Observables <a name="observables"></a>

The Riot library lets you define Observable objects that are not UI elements:

class MyObservable extends Riot.Observable 
{
   doSomething()
   {
      this.trigger("something-done");
   }
}

var a = new MyObservable();

a.on("something-done", ()=> {
   console.log("something has been done on 'a'!");
});

Router <a name="router"></a>

The Riot library comes also with a minimalistic router, the API is the same also in TypeScript.

Example:

// install router hash changed function
riot.route((id,username) =>
{
   console.log(`hash changed to: id=${id} username=${username}`);
});

// start the router
riot.route.start();

// route to an address
riot.route("welcome/nino.porcino");

// stop the router
riot.route.stop();

Examples <a name="examples"></a>

More example can be found here nippur72/RiotTS-helloworld.

A timer-based counter element <a name="timer_example"></a>

@Riot.template(`
<timer>
   <div>
      timer: { time }<br>
      trasclusion is '<yield/>'<br>                 
      <div each="{el in mylist}">iterating over array item "{el}"<br></div>
   </div>
</timer>`)

class Timer extends Riot.Element {
   time: number;
   timerHandle: number;  

   mylist = ["one","two","three"];
   
   // initializes the element with opts
   constructor(opts) {      
      super();
      this.time=opts.time||0;
      this.timerHandle=setInterval(() => this.ticks(),1000);      
   }
      
   ticks() {
      this.time++;
      this.update();  // refreshes the element
   }

   // handles the unmount event 
   unmounted()   
   {         
      clearInterval(this.timerHandle);
   }
}

Then install the element and mount on the page with:

riot.mount('*');   // mounts all elements

Precompiled tags <a name="precompiled"></a>

To speed up application startup times, tags can be precompiled in order to avoid compiling them at runtime.

Precompiled tags can be also concatenated into a single JavaScript file, avoiding to raise an HTTP request for each tag.

RiotTS can be switched to use precompiled files very easily by just including them (via <script src="..."></script>).

If the precompiled file is included, RiotTS will just use it, otherwise it will load tags via HTTP and compile them at runtime.

Precompiled files are normally intended for production, during the development they are usually turned off for a faster REPL cycle.

Precompiled files are generated with the grunt task grunt-riotts-precompile.

How to setup the task:

$ npm install -g grunt-cli
$ npm install grunt --save-dev
$ npm install grunt-riotts-precompile --save-dev
module.exports = function(grunt) {      
   grunt.initConfig({
      precompileTags: {
         src: ['tags/**/*.html'],
         dest: 'precompiled-tags.js',
         indexByTagName: false
      }
   });
   
   grunt.loadNpmTasks('grunt-riotts-precompile');
   
   grunt.registerTask('default', ['precompileTags']);  
   grunt.registerTask('off', ['precompileTags:off']);
};

How to run the task according to the above Gruntfiles.js:

The generate precompiled-tags.js file can be inlined with a <script> tag just before loading the element's code, e.g.:

   <script type="text/javascript" src="precompiled-tags.js"></script>
   <script type="text/javascript" src="elements/mytag.js"></script>

Precompiled tags can be easily turned off by just commenting the inclusion:

   <!-- <script type="text/javascript" src="precompiled-tags.js"></script> -->
   <script type="text/javascript" src="elements/mytag.js"></script>

or by just emptying the file precompiled-tags.js.

The indexByTagName option:

If indexByTagName is set to true compiled tags will be indexed by tagname rather than file path. So it will be possible to define them in RiotTS with just

@template("mytag")

instead of:

@template("mytagdirectory/mytag.html")

Note: Tags defined by tag name rather than path cannot work if precompiled tags are turned off.

Contributing <a name="contributing"></a>

Contributions are welcome.

If you find bugs or want to improve it, just send a pull request.

Change log <a name="changelog"></a>