Binding with rivets.js – details and tricks

This is the 2nd part of knowledge about rivets.js – a small JS library that binds data in web. While the first one was an introduction, this one treats about the details.

Part 1: My small secret web weapon to bind things – rivets.js

Version!

First of all, use the latest version. The main website is not always up to date so go straight to rivets.bundled.js on GitHub and use that.

In time of writing this, there is one bugfix that tackles the bug where $ variable would be always treated as it was jQuery and failing to run.

→ the bug: issue #646

→ the CHANGELOG

There is also an interesting fork made by blikblum.

Using components

Component in this library is a definition of template! Unlike the rest of library it doesn’t just bind things to existing DOM but creates the DOM based on your template.

Let’s have a scope with a value that will be passed to component:

Then, the definition of component:

How do we use this component?

The <my-component> element will be automatically translated into template:  <div>The title: {data.title}</div> .

It’s also worth to mention the data variable here. It’s defined in component’s initialize(el, attrs) method and then used in the template. I have defined it here due to certain bug that will be discussed in the chapter “Nested scopes don’t refresh?”

Don’t store template in JavaScript code

Instead of defining:

you can generate the template in <template> tag (with PHP or whatever backend language):

then, access the template by id:

The <template> tag is safe since it will be ignored by browser renderer. But if you’re still afraid that it may display in older browsers then put it into <head>  of page.

Live example

Straight to live example of this component:
rivets.js: component usage and update

If you have some questions about components you may want to read this topic:
→ Issue #691

Formatters!

Typically, formatter is used to… format data! For instance, making sure that value is displayed in certain way:

or:

I’ll show you few more.

Call functions only explicitly

If you pass some function as a value to binder it will be treated just as value. In older rivets versions, if some binder like rv-value received a value which was instanceof Function then it would be immediately called. Now, in rivets 0.9.0 it’s turned off by default but can be enabled for legacy code:

If you do need to call such function you’ll need to use the  call  formatter:

Older versions (below 0.9.0) needed a formatter which is now is built-in:

Note: this rule does not apply to rv-on-*  binder, e.g. with rv-on-click="notify"  the notify function will be called on each click. You don’t need the call  formatter here!

→ rivets.js documentation: Call functions

Is a value one of list’s?

I wanted to check if value is any of specific values that are enlisted statically:

And here’s  anyOf formatter that supports it:

where IndexOf  is a special implementation of mine, the code itself should speak for itself:

Get property of object

Since JavaScript expressions are not valid in binders you can’t simply go with:

rv-value="someObject['someProperty']"

Instead, you’ll need a formatter:

that will allow you to access properties:

rv-value="someObject | prop 'someProperty'"

Get indexed element

Same as with previous:

With this someArray[15] would be accessed with:

rv-value="someArray | index 15"

Custom binders

If you’ve used AngularJS then you may be familiar with a concept of directives. In rivets you may treat binder as a modification of DOM elements without using aconcept of components.

A dropdown

Here’s one of my custom binders:

and usage:

Notice the model.badgeColor  that is passed as an argument to binder.

rv-on-* with event cancellation

Usage is similiar to the original rv-on-*:

Warn me about unknown binders

Here’s a trick I like to have on board. In my opinion this small modification should be built-in.

Instead of allowing to define binding for custom attributes like rv-disabled for checkbox you should be only allow to define it with attr prefix – rv-attr-disabled . Why? Just to be sure that your custom bindings are not treated as attributes! Thus, we will overwrite custom rv-*  binder and introduce rv-attr-* .

Now, we can easily bind every single attribute without looking at built-in binder list.

Nested scopes don’t refresh?

There is an issue about internal registration on correct scope. Thus, nested scope may break since they don’t always detect changes in model.

This fiddle shows the problem and this one workarounds it.

The problem seems to be about primitive values like numbers or strings. The easy workaround is to just put everything into an object so rivets can properly react to the changes in the scope.

So, instead of binding this:

rivets.bind(appEl, model)

bind this:

rivets.bind(appEl, { vm: model })

so primitives will be accessed with rv-if="vm.someValue" .

This was reported here:
→ GH issue #512: nested rv-if binders mess

and here, where it’s more investigated:
GH issue #486: rv-each loses reference to parent view model

and potentially fixed in:
commit in a rivets fork made by blikblum

Nuance about this keyword: class vs object

In C#, Java, C++ or other languages this keyword always references to the instance of a class when used in methods. JavaScript is different. Few years ago it didn’t have a mention of classes. JavaScript is all about functions and prototypes. Function can be called in a context and this is what this references to – call context.

To know more about calling a function in chosen context read the manual about the Function.prototype.apply() function. In fact, that’s what rv-on-* binder uses internally.

Let’s have a template which can call a function on click:

Now, let’s create a model (root scope) to bind with <div id="app"> :

Then, let’s deal with the model. If you want to define your model in a class then you can do this in several ways. Let’s start with TypeScript since it has some syntax that ES6 doesn’t:

This TypeScript snippet would be transpiled to ES6 like this:

Have you spotted what happend? shoot was idefined in constructor, not in class! The important part here is the arrow function. It automatically makes a closure that will treat this keyword to be context-independent. If we would specify this method with a function() { }  syntax then this would be depending on context call. Thus, the arrow function syntax is immune to Function.prototype.apply()!

If you need ES5 syntax (without a class) then the other way to define the model / controller would be creating simply an object:

Full test can be found on this plunker:
→ Plunker: scoping and functions

But! There is an option to slightly fix things with this snippet:

You may try pasting it before bind() call in the plunker above and see what this  refers to in each example.

Summary

Is that it? Well, there are features I didn’t discover but documentation does it well enough:

Other than that, this library is really neat. It’s not a framework so it’s easy to be used within legacy projects where views are generated with some PHP code. You may want to add some life with JavaScript into your project without taking jQuery approach. rivets will at least give you some notion of declarativeness.

  • Pingback: My small secret web weapon to bind things – rivets.js – NamekDev()

  • As this article is fairly new compare to others in the internet. Do you use rivetsJS for production and commercial clients? Looking at the repository it looks like the repository isn’t maintained anymore?

    • Well, I did at the time. But since then I have changed my course of career, now I’m not doing web anymore as a job. After hours I use the Elm language because it’s much more stable than JavaScript.