Getting Started
Ripple is similar to other view libraries like React, Ractive, and Vue in that it handles automatically updating the DOM when the state of the view changes.
The main difference with ripple is that it has an nice API, free from global objects, and can easily be extended with custom functionality. Ripple just provides the foundation, you build and customize it from there.
Package Managers
It's highly recommended that you use Component when using ripple. This gives you ability to require templates as strings and easily include plugins. Ripple is built on the idea that it provides a small base for creating reactive views and then you extend each view with plugins.
If you're using Component, you'll be requiring ripple in your module:
var ripple = require('ripple');
If you're not using Component, you'll have to include the standalone version and you'll be working with a global variable:
window.ripple
Templates
Templates in ripple are just plain HTML. They use special expressions within curly braces, a little bit like Handlebars.
<div class="{{type}}">
<img src="{{avatar}}" />
{{ firstName + " " + lastName }}
</div>
Within these braces you have access to properties on the view. Each view has a set of properties stored as a plain object that is observed for changes. For example, the above template might have a view with the data:
{
"type": "winner",
"avatar": "http://ragefaces.io/rage.png",
"firstName": "Nicolas",
"lastName": "Cage"
}
When we set this object and the template on the view, they will be bound together by ripple and any changes you make to this data will be automatically updated in the DOM. Within the braces in the templates you are able to write any JavaScript you want and it will be re-rendered when data changes.
To add templates to your views when using Component, you can just add the template to your component.json
file and you are able to require it:
{
"name": "my-component",
...
"templates": ["template.html"]
...
}
And then require it:
var template = require('/template.html');
var View = ripple(template);
If you're not using Component, you can add templates to your page in script tags and reference them using CSS selectors:
<script type="text/template" id="template">
<div>{{name}}</div>
</script>
And then create a view:
var View = ripple('#template');
Creating Views
When using ripple, you will create views that represent a single DOM element on the page. These views have a template and data. When we render views, the template is transformed into a DOM element using the data. Whenever that data changes, the DOM is automatically updated. Ideally, you should never have to touch the DOM.
Let's create a View using the ripple function:
var View = ripple('<div>{{name}}</div>');
This is creating a new class for this template. Each view in ripple has it's own compiler and set of bindings. This means we can safely extend the view with new functionality without affecting other views. This encapsulation means you can also share your created views with people not even using ripple.
You can also pass in a DOM selector or an element as a template:
var View = ripple('#template');
var View = ripple(document.querySelector('#element'));
This will use the innerHTML of the element as a template. This makes it easy to use <script> tags on your page for the templates.
Now we want to create an instance of that view. This will create the element we can add to the DOM:
var person = new View({
data: {
name: 'Fred'
}
});
person.appendTo(document.body);
When creating a view it takes an options object. The important option here is
data. This is the data we want to render in the template.
You can change the name of this property by using View.parse. This lets you customize the
options that can be passed into the view and where it gets the data from.
View.parse(function(options){
return {
size: 50,
time: options.startTime || new Date()
};
});
You can use this method to set the intial state of the view and set defaults. You can
only add one parse function per view. The options passed in are whatever was passed
to the constructor - new View(options). By default, this just returns options.data.
Working with the DOM
Now that you've got your view you'll want to add it to the DOM. There are a couple of helper methods that can be used:
appendToreplacebeforeafter
var person = new View();
person.appendTo(document.body);
person.replace('.replace-this');
person.before('#something');
As you can see, each method takes an element as the first parameter. This can also be a CSS selector to make things easy.
These will fire the mounted event on the view. If you add the view to the DOM manually,
like this:
document.body.appendChild(view.el);
It won't fire the event. So if you're doing this, make sure you know what you're doing.
It's worth noting that with ripple you should almost never need to touch the DOM itself. This should all be handled through bindings. This makes it extremely easy to test views in isolation without needing the DOM at all.
Lifecycle Events
Views have a number of lifecycle events that you can hook into to modify it's state and add other logic. These lifecycle events are loosely based on Polymer and the Web Components spec.
They are:
construct- When the view is first created and nothing has been setupcreated- The view has been setup but hasn't been renderedready- The view is rendered and the data is being watchedmounted- Any time the view enters the DOMunmounted- Any time the view leaves the DOMdestroyed- The view is removed and all bindings are destroyed
All views, even outside of ripple, have these various states. Normally you code build the logic for these events manually each time. The majority of view logic will occur during these events.
To hook into them, you can use the .on method:
View.on('created', function(view){
console.log(view);
});
var myView = new View();
This event will be fired and passed the view that was just created. In this example, view will
be the myView instance. From here you can do things like setting up validation, creating intervals,
or updating the state.
Just like any other event emitter, you can do .off to remove listeners or .once to only fire
a listener once.
Data-binding
The data object on the view is what is bound to the DOM. You can use in
the expressions within the templates to render
this data and it will be automatically updated when it changes.
person.set('name', 'Barry');
This sets the name attribute on the view and will automatically update any part
of the template that is using name. Actually, it updates any expression that
contains the name attribute, like:
{{ name + " " + lastName }}
You can use these expressions within your templates in attributes or in text:
<div class="{{type}}">
<img src="{{avatar}}" />
{{ firstName + " " + lastName }}
</div>
You can also use them to pass data to child views.
Behind the scenes ripple creates attribute and text bindings for these expressions and listens for changes to the properties that are used inside of them.
The expressions are called in the context of the view itself. So you have access
to view methods and properties. For example, the event plugin
adds an on-click directive.
<button on-click="{{ this.save }}">Save</button>
Getting and setting values
You can get a value from the view by using .get and set it using .set:
person.get('name');
These also allow you to get and set nested properties:
person.get('links.facebook.url');
person.set('links.facebook.username', 'nicolascage');
You can also get values directly from the data object:
person.data.name
There are a couple of catches when accessing it from the data object directly:
- You can't set values because the changes won't be reflected in the DOM
- It won't look up values from it's scope
Generally, these limitations aren't too much of a problem. Here is an example of where you might use it from within your view:
Person.prototype.toggle = function(){
this.set('open', !this.data.open);
};