RView
is a companion View library for building web applications. On the opposite of React, VueJS and Angular, View focuses on the viewing operations only. Besides, RView
has a very fast learning curve as it doesn't introduce any new directives, pseudo-code, build, etc.
Thus, RView
is only intended to create web components, insert them into the DOM and update them.
RView
allows you to create and modify an HTML template. When you apply a change to your template, RView
checks the differences between your template and its counterpart in the DOM. It updates only the elements that have been modified.
You don't have to create a build environment for RView
. You just need an HTML
file with a script:
<script src='https://www.unpkg.com/@mobilabs/rview/_dist/lib/rview.min.js'></script>
<script>
const { h } = RView;
const { Component } = RView;
const { render } = RView;
...
</script>
Or:
<script type=module>
import { h, Component, render } from 'https://www.unpkg.com/@mobilabs/rview?module';
...
</script>
Then, you can create your first component:
<script type=module>
import { h, Component, render } from 'https://www.unpkg.com/@mobilabs/rview?module';
const C = Component({
$render() {
return '<h1>Hi!</h1>';
},
});
</script>
In its minimalist form, a rview component
requires just a $render
method that returns an XMLString.
When your component is ready, you can insert it in the DOM:
<script type=module>
import { h, Component, render } from 'https://www.unpkg.com/@mobilabs/rview?module';
const C = Component({
$render() {
return '<h1>Hi!</h1>';
},
});
render({
el: '#app',
children: { '<C />': C },
template: `
<C />
`,
});
</script>
render
requires an object with three properties:
-
el
: it defines the anchor point of your component in the DOM, -
children
: it contains an object that makes the link between the HTML tag and therview component
, -
template
: the HTML template that is rendered in the DOM.
And this is what you get in the DOM:
<div id="app">
<div id="i67c63gj">
<div id="i354anrd">
<h1>Hi!</h1>
</div>
</div>
</div>
Simple isn't!
But of course, you can do much more. You can encapsulate a component inside another component, you can interact with a component, you can send and receive messages from components, you can create animated components, etc.
const App = Component({
$render() {
return `
<h1>Hello World</h1>
`;
},
});
render({
el: '#app',
children: { '<App />': App },
template: '<App />',
});
This isn't really new. You already see a similar example in the previous chapter.
const App = Component({
$render() {
return h('h1', { class: 'myclassname' }, 'Hello World');
},
});
render({
el: '#app',
children: { '<App />': App },
template: '<App />',
});
RView
supports the hyperscript
language. You can use it to describe your component if you wish.
This is an example that shows how to update a text based on DOM events
:
const App = Component({
$init() {
this.state.heading = 'Hello World!';
},
$listenDOM() {
this.$('form').on('submit', (e) => {
e.preventDefault();
const { value } = e.target[0];
this.$setState({ heading: value });
});
},
$render(state, props) {
return `
<div>
<h1>${state.heading}</h1>
<form>
<input type="text" />
<button type="submit">Update</button>
</form>
</div>
`;
},
});
render({
el: '#app',
children: { '<App />': App },
template: '<App />',
});
Your component has two more methods $init
and $listenDOM
.
$init
is called before your component is rendered into the DOM. You can use it to update some parameters.
$listenDOM
is called after your component has been rendered into the DOM. You can use it to execute functions that listen for DOM events.
Here we use $init
to add and to initialize the property heading
of the object state
(that is an empty object). And, we use $listenDOM
to execute a function that listens for DOM events (if you are a bit familiar with jQuery
you can easily decrypt this function).
$('form')
selects the DOM child element form
of your RView Component
- and only your component, it can't select DOM elements outside the scope of the component you defined. .on('click', (e) =>)
listens for a click event. And, it calls the callback function, you defined, when this event occurs.
This callback captures the value entered by the user and execute $setState
. $setState
updates the value this.state.heading
and calls a RView
function that compares your updated template with the DOM element and updates the differences.
const Clock = Component({
$init() {
this.state.time = Date.now();
// update time every second
this.timer = setInterval(() => {
this.$setState({ time: Date.now() });
}, 1000);
},
$render(state, props) {
const time = new Date(state.time).toLocaleTimeString();
return `
<div>
<span>${time}</span>
</div>
`;
},
});
render({
el: '#app',
children: { '<Clock />': Clock },
template: '<Clock />',
});
Here we created a clock that is incremented every second. You should easily understand how it works as it doesn't introduce new concepts.
Now, we are going to create a clock
that we can drive
from outside:
const Clock = Component({
$init() {
this.state.time = Date.now();
},
start() {
// update time every second
this.timer = setInterval(() => {
this.$setState({ time: Date.now() });
}, 1000);
},
stop() {
clearInterval(this.timer);
},
$render(state, props) {
const time = new Date(this.state.time).toLocaleTimeString();
return `
<div>
<span>${time}</span>
</div>
`;
},
});
const app = render({
el: '#app',
children: { '<Clock />': Clock },
template: '<Clock />',
});
// Gets the clock web component and then call the custom methods
// 'start' and 'stop'
const clock = app.$getChild('<Clock />');
clock.start();
setTimeout(() => {
clock.stop();
}, 5000);
As you can see, there are two new methods: start
and stop
; a two custom methods.
The start
method includes the code that was previously in the $init
method to start the timer. And, the stop
method stops the timer by stopping the Javascript function setInterval
.
You have propably noticed that render
returns a variable. This variable is an object that implement some useful methods.
The method getChild('<Clock />')
returns the object clock
that instantiates the RView Component
Clock
.
Now, with this object, we have access to the RView Component
Clock
. And we can call the methods start
and stop
.
That's all!
RView Component
implements an useful method to move elements. See the code below:
const App = Component({
$init() {
this.state.top = 0;
this.state.left = '100px';
setTimeout(() => {
this.$animate({ top: '500px', left: '800px' }, 'swing', () => {
// callback executed when the animation is over.
});
}, 1000);
},
$render(state) {
return `
<div>
<div class="rect" style="position: absolute; top: ${state.top}; left: ${state.left}; width: 100px; height: 100px; border: 1px solid red;"></div>
</div>`;
},
});
render({
el: '#app',
children: { '<App />': App },
template: '<App />',
});
Here the $init
method implements:
this.$animate({ top: '500px', left: '800px' }, 1000, 'swing', () => {
// callback executed when the animation is over.
});
The method $animate
updates this.state.top
from 0
to 500px
and this.state.left
from 100px
to 800px
. This operation is done in 1000 ms
and the transition function is swing
(see Robert Penner's Easing Functions).
And, RView
takes care to update the style attributes top
and left
of the DOM element rect
when the values of state.top
and state.left
change.
RView
implements a mechanism that allows the components to communicate together. Look at the code:
const Hello = Component({
$init() {
this.state.message = '-';
},
$postRender() {
this.$listen('at:hello:from:hi', (msg) => {
this.$setState({ message: `I got the message: ${msg}` });
});
},
$render(state, props) {
return `
<h1>Hello World</h1>
<h2>${state.message}</h2>
`;
},
});
const Hi = Component({
$postRender() {
setTimeout(() => {
this.$emit('at:hello:from:hi', 'Hi Hello!');
}, 5000);
},
$render() {
return `
<h1>Hi!</h1>
`;
},
});
render({
el: '#app',
children: { '<Hello />': Hello, '<Hi />': Hi },
template: `
<Hello />
<Hi />
`,
});
Here you can see that the component Hello
implements the method $listen
. This method is listening for a message with the signature at:hello:from:hi
. When the message arrives, it updates the property this.state.message
.
The component Hi
sends the message Hi Hello!
5 seconds after it has been attached to the DOM. The message is sent thanks to the method $emit
.
Simple isn't it!
Static Methods | Description
whoami() | returns the library name and version,
h(tag, attribute, value) | converts an hyperscript format to an XML string,
Component(options) | returns the child component constructor,
render({...}) | renders the components in the DOM and returns the root component object,
restore(app), | restores the DOM to its initial state,
remove(app) | removes the RView app from the DOM and delete it (use with care),
plugin({name, ref}) | attaches a plugin,
makeid | returns an unique string pattern,
extends | returns a component inheriting from a parent,
Methods | Description
Empty Methods |
init() | executed before rendering the component in the DOM (to be phased out),
events() | executed after rendering the component in the DOM (to be phased out),
listen() | executed after rendering the component in the DOM (to be phased out),
render() | returns the HTML template,
postRender() | executed after rendering the component in the DOM (to be phased out),
onChange | called after the component is updated in the DOM (to be phased out),
|
$init() | executed before rendering the component in the DOM,
$listenDOM() | executed after rendering the component in the DOM,
$render() | returns the HTML template,
$postRender() | executed after rendering the component in the DOM,
$onChange | called after the component is updated in the DOM,
|
Generic Methods |
$(sel) | returns an object to access to the comp. in the DOM,
$animate() | updates state properties from an initial value to a final value,
$append() | appends a component as the last child,
$getChild(tag/id/name) | returns the matching child object,
$removeChild(tag/id/name) | removes the matching child object,
$getChildren() | returns the list of the first level children,
$getIdAndName() | returns the component's Id and name,
$hyperscript(args) | converts an hyperscript format to an XML string,
$setState() | updates a state value and updates the DOM accordingly,
$listen(event, handler) | listens a message,
$listenOnce(event, handler) | listens a message once,
$emit(event, payload) | sends a message,
Methods | Description
$() | selects the View Component and returns this,
$(sel) | selects the child element with the attribute 'sel' and returns this,
$().id | returns the id of selected element,
$()[0] | returns the selected DOM element,
$().select(el) | selects the child element with the attribute 'sel' and returns this
$().selectChild(n) | selects the nth child,
$().parent() | selects the parent node,
$().firstParent() | selects the root parent node if defined,
$().find(sel) | returns the NodeList of the matching children,
$().tag() | returns the nag name of the selected element,
$(el).innerHTML | returns the HTML content of an element,
$(el).outerHTML | returns the HTML element and all its content,
$(el).text | returns the text content of an element,
$().firstChild() | returns the first child,
$().children() | returns a DOM object with all the node children,
$().childIndex() | returns the child index (0 for the first child),
$(el).getBoundingClientRect() | returns the bounding boxes of an element,
$(el).css(style) | returns the style value,
$(el).getClassList() | returns a DOMTokenList (getClassList() is a wrapper around classList),
$(el).hasClass('class') | returns true if the node has the class 'class' or false if not,
$(el).attr(attribute) | returns the attribute value of the selected element,
$(el).on(event, listener) | adds an event listener to the selected child and returns this.
$(el).off(event, listener) | removes the attached event listener from the selected child and returns this.
$(el).trigger() | fires the event associated to the selected element,
$().remove() | removes the element from the DOM (to handle with care!),
MIT.