Web components in go

I was always terrified by the way the web is build. You must do everything by hand. Want some fancy button? Here is an div in a div in div with four different classes an bunch of javascript.

Also, now I’ve worked my way towards better understanding of css, but the beginnings were very hard. It felt like there are thousands of modifiers, selectors too complicated and I was always styling something just by trial and error.

And don’t get me started on javascript. Everybody knows that: [] + "" === ""

HTML is kinda nice. The DOM is an tree, that’s an great way to create separation between elements. Also the events, that you can listen to and attributes that are a kind of “API” of the element. But HTML is old, and must support previous versions (more or less). And let’s be honest, HTML was originally designed for creating documents, it was not an application platform. Everything now is a div soup anyway.

I’ve always felt better when creating desktop apps. In desktop app you usually use some graphics library, like GTK, Qt or Tkinter. There is an runtime, an event system, and a collection of widgets. You can create your own widgets out of the existing widgets. You got bindings to your favorite language. Everything is nice and modular and the code is (mostly) more readable because you don’t have to create an box in a box in a box to create an fancy button. You can create an FancyButton class that wraps all the boxes and use that new widget when you need it.

But then something magical happened! Someone thought that “hmm … this modularity thing is great, why we don’t have it on web?”

And so web components came to life. It was slow and it isn’t supported on all browsers now, but it runs :) (and there are polyfills). Now you don’t have to use some framework to create interesting web apps. The platform is everything we need. Web components are actually multiple standards:

  • template — it’s an element that you can use to stamp out it’s content, to reuse it when you want to
  • shadowDOM — an way to encapsulate an DOM in a DOM, so it is separated and can’t be changed from the outside
  • custom elements — finally the most important thing, you can register your own element, that has content defined by you, API defined by you and style defined by you
  • html imports and js modules — there was no way to import some stuff declaratively and now you can (this is not so important in this article)

And recently polymer became popular. If you are like me, than you also don’t like big fat frameworks. A framework is good for all sorts of things, but only if you behave the way the framework allows. If you want to do something else, you must fight with it and that’s sometimes difficult. Polymer is different, it is just an library. A thin layer of sugar on top of the web platform. So it’s easier to create web components.

It combines the standards above, so you don’t have to do it by hand, and also adds data bindings, so you don’t have to write bunch of boilerplate to move data from the DOM to your data structure.

But one thing bothered me. Javascript. Yeah, the good old javascript. I know, you are now like “hey! what’s wrong with javascript. the new es6 has classes, and proxies and async/await and stuff”. But I’ve came from an world where types are important, where you can’t add new properties to an object on the fly, where the compiler tells you when something is wrong. But i’m alright with javascript. After all, it’s a language that was designed to do simple stuff on the web. Like: when you click here, then add some class there or so. And i’m not the only one who thinks that javascript is terrible.

So now I must confess. I love golang. It’s simplicity, easy concurrency, interfaces and the general philosophy around it. It’s a great language. And there is an compiler to js, called gopherjs, so you can write go and run it in the browser. So there have to be a way to do web components with go!

And now *drum roll*, ladies and gentleman, let me introduce you golymer! An library, written from sketch in go, which enables you to create web components entirely in go. It has everything you need!

  • wraps your go struct so that it can be registered as a new custom element
  • stamps out your template to the shadow DOM of your element
  • maps the exported fields of your struct to the element attributes
  • has built in data bindings
  • has declarative event listening
  • field observers

This was an long intro, let’s do some code! We will build an fancy clock element, and we can use the fancy go things! Like go’s standard library and goroutines.

We create an template for our fancy clock element. There is just a style element and the current time to display. The time field is data binded to the template and every time the fields value will change, the text node in the template will also change. This will create the template element that will be used to stamp out the contents of our element on it’s creation in the DOM.

Next we create a struct that represents the clock element. It must embed the golymer.Element it’s name will be converted from CamelCase to kebab-case. So our FancyClock struct will be <fancy-clock> element.

Exported field Format will be mapped to an attribute of the element. Other fields will not be synchronized with the attributes.

We must also create an constructor of the element. It must be an function with no arguments and must return an pointer to the struct. Here we set the template of the element.

Our fancy clock will tick every second and it will update the actual time. Custom elements have some default life cycle callbacks. The ConnectedCallback is called when the element is connected to the DOM. We must call the default golymer.Element.ConnectedCallback() and also we can spin up a goroutine that will update the time or it will exit when it gets an signal from the done channel.

Notice that we just assign the new time to the time field an don’t have to worry about that the changes will be reflected to the DOM. Golymer is doing some magic behind the scenes and captures the changes of the field and then actualizes the data bindings in the template.

DisconnectedCallback is called when the element is removed from the DOM. There we send the done signal to stop the goroutine. Like every good gopher, we also must know when our goroutines start and also when they will end.

And finally, an very important thing! We must introduce our new element to the browser. We do this with golymer.Define that takes the constructor of the element.

Like every go code, to run this piece of code, we must compile it. Gopherjs will build our package to one single file with no dependencies (like go build). If you are creating an app, with multiple custom elements, I’ll recommend to add all of them in one package and compile it to one js file. Building the js file is easy:

$ gopherjs build

Now include the js file to your index.html and you can use the new element in the regular html.

the <fancy-clock> element

Isn’t it great? We now have an new custom element clock that ticks, can be formated by changing the format attribute (with the golang reference time), we could use the amazing goroutines and channels in the browser. We didn’t have to write a single line of javascript or write some ugly boilerplate code for the DOM manipulation. All relatively lightweight and using the platform.

So use the platform and use it with go!

Golymer also brings some common reusable custom elements (work in progress). There are some material design components and also some elements that will help you to declaratively manipulate the DOM.

Like the <dom-repeat> element that will stamp out an slice of objects with an delegate element.

<dom-repeat items="{{ItemsSlice}}" delegate="item-element"></dom-repeat>

Or the <dom-switch> that will show only one of it’s child elements by the case attribute value.

<dom-switch expr="[[page]]">
<div id="div1" case="div1">1</div>
<div id="div2" case="div2">2</div>
<div id="div3" case="div3">3</div>
</dom-switch>

By changing the page field the <dom-switch> element will show the corresponding child div.

I think this is everything you have to know to start working with golymer. I’m thankful that you’ve been reading this far and hope that you will give a try and build some interesting custom element in go with golymer :)