Rendering Skills from JSON - jannino.com

Rendering Skills from JSON

Written by Joseph Annino

Up at the top of the home page is a nice little list of my skills. There are quite a few of them, and each is a button, so that’s quite a bit of redundant html. Programmers hate repeating themselves, I thought this would be a great place to improve the site and make this driven from a JSON data file.

JSON

An acronym for JavaScript Object Notation, JSON has become the lingua franca of web data. A subset of JavasScript, its a really simple file format that translated directly into a simple in memory representation in lots of languages. Parsers and serializes for it exist for just about every language, which makes it great for sharing data.

JSON in Wintersmith

This is in built using Wintersmith, a node.js based static site generator, which uses Jade templates. When building a site, Wintersmith looks through the contents directory in your site and turns any files in there into JavaScript objects in memory, and places them under a variables named contents. Inside there, is an object for each file, where the filename is the key. located under sub-keys for any parent directories. Wintersmith converts all files into an in memory representation available from your templates, using a system of plugins to read the file. So for instance, it did that to the article you are reading right now.

For JSON files, what isn’t so obvious, is that Wintersmith will place the parsed contents of the file under a metadata sub-key. So for the file contents/data/skills.json that I added, the data lives at contents.data['skills.json'].metadata.

A list of skills

For the data itself, I am using an array of objects, with keys name, url, and childrenn, which is an array which may recursively have the same format. I am using an array instead of making everything an object because you must use arrays in order to maintain the ordering of the contents. Here in a example of a piece of the data:

[
  {
    "label": "HTML5",
    "url": "http://www.html5rocks.com/en/",
    "children": [
      {"label": "css3", "url": "http://www.w3schools.com/css/css3_intro.asp"},
      {"label": "bootstrap", "url": "http://getbootstrap.com/"},
      {"label": "less", "url": "http://lesscss.org/"},
      {"label": "sass", "url": "http://sass-lang.com/"}
    ]
  },
  ...
]

Looping

Once I know where the data is, the template index.Jade needs to be edited to loop over it and build the list of skills.

In the original version of the page, I had a lot of redundant code that looked like this:

          li: +btn-small("HTML5","http://www.html5rocks.com/en/")
            ul
              li: +btn-small("css3","http://www.w3schools.com/css/css3_intro.asp")
              li: +btn-small("bootstrap","http://getbootstrap.com/")
              li: +btn-small("less","http://lesscss.org/")
              li: +btn-small("sass","http://sass-lang.com/")

Ok, so maybe that doesn’t look much worse than the JSON above, but this code does have some limitations. If I wanted to render this using different html tags, or add fields to some or all of the skills, I would have to edit a lot of stuff in the Jade template. Also the idea of templates is to be about structure, not data. A good template is about reading data into a structure, so the data can change and your structure adapts.

The one good thing in the existing code, is I was already using a Jade mixin I wrote called +btn-small. More on that later.

So using the above JSON, I can re-factor all of this into:

        ul
          each pitem in contents.data['skills.json'].metadata
            li: +btn-small(pitem.label, pitem.url)
              ul
                each citem in pitem.children
                  li: +btn-small(citem.label, citem.url)

Jade has some great control structures built in. One of them is each, to let you iterate over an array or object. Here I have the two levels of array, and I pass the contents within each into the =+btn-small= Jade mixin which renders a bootstrap button. So a whole lot of redundant code is reduced down to two nested loops that are pretty short.

Mixing it in

+btn.small is a Jade mixin. Basically, its like a function (internally, Jade really does make a function) which given some inputs will render a bit of Jade template.

I put all my mixin definitions in templates/mixin.Jade, and then added the line include mixin to the top of templates/index.Jade.

mixin btn-small(label,href)
  if href
    a(type="button",href=href,attributes).btn.btn-default.btn-xs #{label}
  else
    button(type="button",attributes).btn.btn-default.btn-xs #{label}
  block

mixin btn-default(label,href)
  if href
    a(type="button",href=href,attributes).btn.btn-default #{label}
  else
    button(type="button",attributes).btn.btn-default #{label}
  block

I define two mixins, btn-small for small buttons and btn-default for default buttons. They use a bit of Jade’s control structures to render buttons with an without links as the appropriate element, either a or button. Also, the attributes variable set by Jade will pass through any extra attributes set of the mixin when it used in a template.

Bringing it all together

Bringing together Wintersmith and Jade with some JSON its possible to remove a lot of redundancy and add quite a bit of flexibility. That JSON data could just have easily come from a dynamic source like a REST web service. All of this together, brings a lot of power and flexibility.