Before you go, check out these stories!

0
Hackernoon logoFront-end framework attack: Riotjs product customiser by@birdyboy18

Front-end framework attack: Riotjs product customiser

Author profile picture

@birdyboy18Paul Bird

So it’s been the first week of the challenge, and I’ve had the fun of building a product customiser using riotjs. Read along as I talk about how I found it.

Feeling impatient — here is the demo and files (also useful if you want to code along)

So here it is, my first pick:

picking riotjs and product customiser
And what I built

Learning Riotjs

This was actually a very nice one to get started with, this was because the concepts it introduced were not all that new to me. Riot.js boasted itself as a “simple and elegant component-based UI library”

This was true, one file with all the html, js logic and css all in the same place made it very easy to edit and write; You could then use, said newly made tag in your html like a normal html tag. This isn’t too dis-similar to how most modern js frameworks currently work.

It has a super simple API, the templating syntax was easy, passing data around was straightforward, the observable api was a big help and knowing how it updated all led to an overall quite straight forward project build.

I did run into a few hiccups but I’ll cover those later. I’m certain this was due to my own lack of knowledge more so than any issues with the library. I’m also no expert on it, so first and foremost the best place to look for answers is the riot docs themselves.

Setting up

The first thing I definitely wanted to do was to set up so I could use the latest javascript syntax. I got to work installing webpack and any needed loaders that riot.js might use, luckily the documentation site covered briefly how to get a webpack and es6 setup working.

It’s worth noting I didn’t have to set it up like this. In-fact it’s very easy to make each tag a separate script, add the riot.js compiler script and let it handle the rest in browser. The team have done a great job. This already makes it a great choice as a drop in style for templates/wordpress sites. This was one of my original investigation criteria in my first article.

First up, a classic npm install. npm i -D webpack babel-core babel-loader babel-preset-env riot riot-compiler riot-tag-loader

Then a fairly standard webpack config. You can change this to however you like. It’s important that the tag loader is there so webpack knows how to deal with riot tags.

webpack.config.js

module.exports = {
entry: './src/js/app.js',
output: {
filename: './assets/js/main.js',
},
module: {
rules: [
{
test: /\.tag$/,
loader: 'riot-tag-loader',
enforce: 'pre',
query: {
type: 'es6'
}
}, {
test: /\.(js|tag)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['babel-preset-env']
}
}
}
]
}
}

To get this to work all I did was run webpack --watch in terminal and that was it for js compilation.

HTML/CSS
The html is also pretty simple. I included a css library called tailwind that uses utility classes as I wanted to have some styles I could get started with. I ended up writing my own css too, but very little. This didn’t really need much. It’s only a small experiment/webapp.

I served this up locally using the node http-server module from npm which I use for any static file server needs. This is all I wanted, I didn’t need to have hot module reloading. This was enough to get going, and served its purpose.

Overall, pretty straightforward.

Starting the Riot

To start rendering a riot tag, it’s really easy, import the global riot instance, and any riot tags that you would want to render into the app. In the app.js you would write this.

app.js

import riot from 'riot'
import HelloWorld from './components/HelloWorld.tag'
riot.mount('*')

Here we’re using a wildcard, which means render all tags it finds on the page. You could also choose to render just the one tag by changing it to the tag name riot.mount('hello-world')

components/HelloWorld.tag

<hello-world>
<p>Hello World</p>
  <script>
console.log('Hello from hello world tag')
</script>
</hello-world>

You’ll see above that to create a tag, it’s really easy; When you make a tag you declare the html first and then the script tags. You’re also able to add css into this file too by using style tags, however I’m still a fan of including it all into one css file.

If you’re interested, the riot docs cover this in more detail.

Then finally, you would edit the html to have the riot/custom tag in the html, and a script link to include your compiled js bundle.

HTML

<html>
<head>
<title>Riot | Riot Test</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss/dist/preflight.min.css" rel="stylesheet">
<!-- Any of your own CSS would go here -->
<link rel="stylesheet" href="assets/css/style.css">
<link href="https://cdn.jsdelivr.net/npm/tailwindcss/dist/utilities.min.css" rel="stylesheet">
</head>
<body class="font-sans">
<div class="container mx-auto">
<hello-world></hello-world>
</div>
<script src="assets/js/main.js"></script>
</body>
</html>

On screen you should hopefully have a page that says “Hello World” and a log statement in console. That was pretty easy aye. Lets make the message dynamic.

Any attribute you put on the custom tag that doesn’t fall under a reserved attribute will get passed into the scope for you attached to the lexical ‘this’ under a key called opts. Modifying the tag we can now do this.

components/HelloWorld.tag

<hello-world>
<p>{this.opts.message}</p>
<script>
console.log(`${this.opts.message}`)
</script>
</hello-world>

HTML

<hello-world message="Hello from above"></hello-world>

You can see that, suddenly it’s easy to pass data down. You’ll find this paradigm of uni-directional data flow also exists in React and Vue.

Riot also allows you to pass in a javascript object when you mount your tags, this will be made available to the root tag (because you can nest tags). This will be made available to the opts key on that tag instance. The object doesn’t just have to be a key:value, it could also be a function, meaning you can pass entire classes and functions into your tags which makes it quite powerful. We can use this to pass data to our tags.

app.js

import riot from 'riot'
import HelloWorld from './components/HelloWorld.tag'
riot.mount('*', { message: "Hello from initial data"})

Then we need to modify our tag instance and html to use this.

HTML

<hello-world></hello-world>

Here we’ve removed the message attribute, the initial data will now be handled and passed down. This would be different if say, hello world was nested within a parent tag. We didn’t need to change anything in the tag this time. This is because all this data will be passed to opts to any direct descendants of the parent tag.

Now we’ll make a new tag called <hello-container></hello-container> to demonstrate passing data down to tags that are descendants of the top level tag.

app.js

import riot from 'riot'
import HelloWorld from './components/HelloWorld.tag'
import HelloContainer from './components/HelloContainer.tag'
riot.mount('*', { message: 'Hello from above'})

components/HelloContainer.tag

<hello-container>
<hello-world message={this.opts.message}></hello-world>
</hello-container>

Here the hello-container has our hello-world tag nested inside it. This is all the tag needs to be, this time however, we’ve passed the message from the parent tag/container down to a nested hello world tag. It’s then accessible as before via this.opts.message .

Making the customiser

Now that the basics of how to make and use tags has been covered. It’s time to start making the customiser. Here is what I made, shout out to my good friend and housemate Matthew Hughes, for making the assets for me.

Finished result of the riot js customiser

The idea is that clicking on either of the options in the sidebar will change what our rioter is wearing. While quite trivial, it demonstrates how to pass data between various tags and how riot really easily enables us to do this.

First up was to make the sidebar tags and a parent tag. They are as follows. We’ll be making these in a bit.

  • product-customiser
  • <sidebar></sidebar>
  • <colour-list></colour-list>
  • <colour></colour>

app.js

import riot from 'riot'
import ProductCustomier from './components/ProductCustomiser.tag'
import sidebar from './components/sidebar.tag'
import ColourList from './components/ColourList.tag'
import Colour from './components/Colour.tag'
const initialState = {
options: {
head: [
{
name: 'Spotty Bandana',
thumbnail: 'bandana',
layerSrc: 'bandana',
hex: '#D92222'
}
],
}
}
riot.mount('product-customiser', initialState);

Using the above data structure, we can then take these options for each head, torso and leg layers and have the options as an array inside. If you want to see the finished source file you can check out how it was structured in the end, and how many options I added, it didn’t change from the above. File is here.

So that it’s easier for you to understand and I don’t have to fight Medium’s line wrapping, I’m going to post mainly pictures of the code snippets over typing them. Also, syntax highlighting.

components/ProductCustomiser.tag

Pass the options from our initial data from riot.mount

components/sidebar.tag

sidebar tag with nested colour-list tag being passed our options again to colours

For each group we pass in the options array for the head, torso and legs to the colour-list tag. We then use these to render a colour tag for each entry using the riot.js loop syntax. We also pass in an attribute called layerName, which we use later.

components/ColourList.tag

Here the colour list tag takes the passed array for each option grouping and then renders a tag for each one.

When rendering a tag using the loop it will automatically pass any data from the object to that tag instance scoped to this as a quick example layerSrc will be this.layerSrc this is instead of being put under this.opts.layerSrc like you might think. For for reference on loops check the docs.

Then its a case of using the passed down data to render a colour for each one.

components/Colour.tag

Here we use the data to set a background using css

In riot you can set the styles by using an object which it converts to strings. here, if for any reason the background thumbnail isn’t there we use the hex value to set a background colour instead of an image.

Now that we’ve managed to pass our data down, we need it to do something. Preferably when you click on it, lets quickly attach an event, build the other part of the app and then come back to here.

components/Colour.tag

Added a changeLayer function to the onlick binding

Next up we need to build what I called the changing-room tag. In order to be able to change how our rioter looks, the images are all placed into a containing div.

It works by having a base image and then each image layer was exported out as a transparent png at the exact same size. These position absolutely over the top of the base one. Then when we want to change the head, torso or legs; we can use the tags properties that are bound in that tag to swap out the images. Let me show you.

components/ChangingRoom.tag

Here we pass active layers down from the opts

Here the layer image sources are set from the opts that are passed down to the tag. This data is added to the root initialState from app.js and passed down each tag as mentioned.

components/ProductCustomiser.tag

We include the new changing room tag and pass in activeLayers

Also don’t forget you’ll need to import the new changing-room tag for riot to pick it up. We also pass the activeLayers to our sidebar as we’re going to need them later.

Finally, here is the new part of our initial state.

app.js

adding the new tag, and new activeLayers object

Here the key refers to layer it belongs to and the value is actually just the filename we want to pull in. You’ll see that these match up in the code of ChangingRoom.tag. These make up the filename in the directory of images. If successful, you should have a chilly looking Dave on your screen. If you are following along and unsure on assets/image directory the structure. Have a quick gander at the source files on github.

Someone get this guy some clothes

To change an item of clothing for Dave, all that is needed is to change the value for either of the layers. We have a problem though? Our components/tags have no idea each other exist.

This is actually good though. If they did, they would be too tightly coupled, when in reality we want them to not care about one another. This has many benefits, such as readability, maintainability and more.

Centralising our state with Observable

I’m going to show you how I implemented the finished solution I came up with for talking between tags, I’ll talk about points where I had hiccups as I go through.

Hiccup 1
I first used the Observable as a very easy event bus, but state very quickly got out of sync. This was because the update was being called before the state was even changed, however on the next click it would update. It worked fine for the changing of the layers but not when I wanted to add active states to the colour tags, it’d always be behind.

An Observable is a pattern where code can listen and run functions on a custom event. It’s a really powerful pattern and we’re going to use it with a little help with the ideas powering the React Redux paradigm.

I wanted to have the state remain in sync always. This meant making my own store. I got the general gist of how Redux worked and implemented a trivial example. My version however didn’t use dispatch, or subscribe. It used the native on and trigger methods provided by riot and directly mutated state. It’s not a lot of code.

Store.js

All the code needed to create our a store class

You can easily extend any function or class to be a riot observable by passing it as a parameter to Riot.observable this extends it to have all of the same methods Riot.observable has. This means now our store can use the observable on and trigger methods and more. See the API for everything it can do.

Hiccup 2
First I was taking a more OOP approach and tried extending Riot Observable and calling super in the constructor on ‘this’. For some reason it attached the methods but didn’t keep track of the subscribers. I therefore did it this way, where I called Riot.observable passing the lexical this of the class to it.

The store can take an initial state and itregisters an event on itself that will call handleAction. This means we can call store.trigger(‘ACTION’, payload) and handleAction will run. It acts similar to a reducer in redux at this point, but all hard coded in. It will match the action and then mutate the state before then triggering an update event.

Finally we set a custom getter for getState, I didn’t have to do this, and could of just call state directly, but I like that this feels more implicit of what I’m doing.

That was it for a store. Now we just needed to instantiate it and feed it to our initialState and pass it down to our tags.

app.js

make a store, feed it our data and pass it to the mount method

Now you could access it and use it as this.opts.store . With the store being available on the tags you would use it to trigger changes, going back to the changeLayer function in Color.tag. You could cause it to trigger an action. Then mutate the state on it.

Here changeLayer now triggers an action in our store

Our store will know how to react to this, and mutate the state.

In the above code snippet where I declare layername and activelayers you can see that we can get the opts from the parent, remember this is something we added on earlier. Getting the opts from parent is something we ahve used yet and every tag has access to it’s parent all the way up to the root tag.

In theory I could of gotten access to the store by traversing up, however passing down is easier to track that arbitrarily calling this.parent.parent and so on.

Now we know the layername, we use this so that we know what layer we want to change the source image for and send it over.

In the store you can see that the change layer action takes a payload that we use to change state

The change layer actions takes the layername from the payload, uses it as the matching key and sets it’s src to be the new src supplied in the payload. On each colourlist tag we rendered we added an attribute called layername — layername=’head’. This is used to select the right layer from the activeLayers and change it. Once it’s done it then blasts out the change event notifying any listeners that the state just changed.

Now it’s possible to listen for the change event in our tags and call the tags update method.

Understanding how this.update() works

There are two important things to remember about update. The first being that it will automatically update when an event handler is called by a user interaction. This means when we call the changeLayer in the Colour tag, it will automatically call an update to re-evaluate all the expressions in that tag.

Secondly when an update it called on a tag it will call updated on all children tags too. These are good to know because we can use them to our advantage. For a more in-depth understanding of what you can do with update, the docs cover it in just the right amount of detail.

Now we know how that works, lets set the store in the changing-room tag to listen for a change.

Here we listen for a change on our store re-update our expressions and call update

This tags now knows what to do whenever our state is updated. Great, now the state remains in sync and the code knows when it has been updated. I felt this could be improved though so this leads to the next hiccup.

Hiccup 3
I wanted to be lazy and not have to remember to pass down my store or remember to subscribe. Remember that an update on a parent tag calls update on all children. What I wanted to make was what is known as a state container in react land. The parent wrapping tag is product-customiser I set about passing all of the state down from the store and then listening for a change and simply re-assigning state and calling update. Unfortunately I couldn’t get this to work. I still don’t know why it doesn’t truthfully.

Using product-customiser as a state container

The new code now uses a state object which I would re-assign and call and update on, in a hope that everything would update. I actually found this didn’t work. The update was called on child tags but it never seemed to think the expressions had changed.

Even though the above would of been great, re-rendering the state every time would of been unnecessary for a larger app, beyond not also getting it to work, I didn’t want to swap back to passing the store back down. A happy middle grounded needed to be met.

Mixing it up

The solution was quite straight forward in the end, and the best outcome I think. I essentially wanted to be able to have a reference to our store on all of the tags without needing to pass it down.

The solution, riot mixins. A mixin is a pattern that allows you to extend an object and add custom functions and properties to it, we did this earlier indirectly using riot observable to make the store. A mixin allows you to add our own methods.

We’ll make a function that accepts our store, passes it to the object and to the init method which is what gets called when the tag gets made. We then tell it to attach the passed store to all tags as this.store.

The storeMixin code

To start using the mixin, we then need to use the riot.mixin method and pass it the storeMixin function we just made. Remember you’ll need to import it, if you want to use it.

Globally setting a mixin

You can either set a mixin to be global like we have here, or you can register a shared one by namespacing it. riot.mixin('myMixin', myMixinObject) Then you call this.mixin('myMixin') in any tags you want to use it in.

A particularly good use case for using a shared mixin would be if you perhaps, wanted to have multiple stores. Then you can have access to stores in tags that you only care about instead of having one giant store.

Now we can simply call this.store and not have to worry about passing the store down to our tags

Now that every tag has access to the store, everything can be run through it and the state can be kept in sync.

In the finished source files, I still listen for change in the product-customiser and update the state, which does update the state that is passed down the tags. It appears that having to manually re-assign makes riot recognise that a change has happened. Since the update call is also made there still, you can get away with omitting this.update() on the change callback for child tags.

Finally, now all that needs to be done is to update the colour tag to use the new store reference and use it to work out the active state.

Here isActive is re-calculated on change, update not needed because it’s called in product-customiser

Now you can also add an isActive class based on it’s value.

add an isActive class if isActive is true or false

If you’ve been actively following along you should hopefully have a fully clothed Dave ready to take on any riot. Here’s the finished demo and source code

Conclusion

I really enjoyed learning Riot.js, it’s a very neat little library. It’s minimal and small API surface made learning how to use it very quick. The fact that it’s not very opinionated on how to do things has been one of my favourite parts of using it. Even though I’ve never used Redux before I was able to take the idea of it and implement it very quickly using riot’s observable API. Which I wanted to do, not because I was told to by riot.

The fact that all the logic is contained within one file for a tag I think is great, and dropping it straight into html and it just working is awesome. This and the Store pattern combined makes it already very lucrative to be used to make advanced widgets and more advanced js functionality for templates. Combine that with passing of data, you could easily bootstrap in the needed data from a CMS with relative ease.

The mixins I felt was really the icing on the cake for me. I thought using that to make a store available to any tag was so powerful and while not an uncommon concept, really cool. Truthfully I’ve just never used mixins very much before as a pattern, so it was nice to get stuck into.

The only criticism I have would be that I’d like to know in more depth how the updates work because I feel the state container approach really should of worked. I think it’d be nicer if the docs told you how it knows to trigger an update and any caveats that may exist beyond the ones already mentioned surrounding updates.

Riot doesn’t force you into a paradigm, it’s easy to learn. It doesn’t need to use ES6 syntax and can be run very quickly in the browser if need be. This makes it a perfect contender for a re-factor for legacy javascript projects, which is one of the things I wanted to consider from this whole investigation into various js libraries.

Tags can be easily decoupled meaning we can still have different parts of the page talk to one another even when using a server rendered template.

Overall I’m already considering this a good contender for a drop in and go approach. That’s all.

If you’ve read this far, thanks for sticking with me. I hope this has helped you, even a little bit. I’m keen to know your thoughts so please do leave messages; I don’t write all that much (might be obvious?). I’ll be building a new project in a new library in the coming week, so try and keep an eye out. Thanks again.

Tags

The Noonification banner

Subscribe to get your daily round-up of top tech stories!