Blog

Vaadin, JS and DOM initialization order

By  
Tapio Aali
·
On Jan 30, 2013 7:47:00 AM
·

As we all probably already know, one of the great new and interesting features of Vaadin 7 is the easy integration of any JavaScript component. No need to write wrappers between GWT and JavaScript anymore, just a simple connector and a state class and you have the freedom to use any kind of JS thingies with your Vaadin application.

But then there are some things that won’t work with Vaadin like they would work with traditional HTML pages. For example, jQuery’s $(document).ready() can’t be used in most cases since it is triggered before Vaadin has actually loaded its components.

Gauges and canvases

I came across this issue when I had to use the Gauge component developed by Mykhailo Stadnyk. The problem was that it requires a canvas element whose DOM id has to be given to it when it is initialized. Therefore you have to be sure that such element exists before the component is drawn.

So I somehow needed to find a way to create a canvas element and then make sure that it is rendered before the gauge. In order to make the system flexible, it was clear that the DOM id must be parameterized so that it can be set from the server-side. A hard-coded id would cause a load of problems, including the fact that there could be only one gauge per page.

The goal was to create a component that allows you to set the DOM id of the canvas element on the server-side and then implement the JavaScript connector so that it creates the corresponding element with the given id.

The simple part: server-side

As expected, the creation of the server-side classes was the easy part. First I implemented the state object that in this case takes care of the synchronization of two variables: the DOM id used and the value of the gauge.

public class GaugeState extends JavaScriptComponentState {
    public int value = 0;
    public String domId;
}

Then I created the actual Gauge class. The @JavaScript annotation is used to load the required JavaScript files: gauge.js containing the Gauge library and gauge_connector.js that is used as a bridge between the library and this class.

Because the id of the canvas element should be set only once, it is given in the constructor and can’t be changed afterwards.

@JavaScript({ "gauge.js", "gauge_connector.js" })
public class Gauge extends AbstractJavaScriptComponent {
    public Gauge(String domId) {
        getState().domId = domId;
    }
    public void setValue(int value) {
        getState().value = value;
    }
    @Override
    protected GaugeState getState() {
        return (GaugeState) super.getState();
    }
}

A bit harder part: JavaScript connector and DOM manipulation

The next step was to create the client-side connector, gauge_connector.js, that receives the parameters from the GaugeState class and applies them to the actual JavaScript component. As a side note, the initialization of the connector can be compared to jQuery’s $(document).ready() function: it is called when the required ‘surroundings’ are ready for the component.

The first thing the connector has to do is to create the canvas element and then append it to the DOM. Luckily, at this point, Vaadin has already created a div element for the component. Therefore we can just create a canvas element, get a DOM id for it from the state object and apply the final result to the pre-existing element. After that the actual Gauge object can be created, giving it the same id that was used with the canvas element. Then the gauge can be drawn.

Also, onStateChange() function was needed so that the value of the gauge can be set from the server-side.

org_vaadin_blog_Gauge = function () {
    var domId = this.getState().domId;
    var canvasElement = document.createElement('canvas');
    canvasElement.setAttribute('id', domId);
    this.getElement().appendChild(canvasElement);
    
    var gauge = new Gauge({
        renderTo: domId,
        value: this.getState().value
    });
    gauge.draw();
    
    this.onStateChange = function () {
        gauge.setValue(this.getState().value);
    };
};

As a result, we can now have two (or even more!) beautifully animated gauges on one page.

Links and afterthoughts

If you want to, you can check out the example project from GitHub. It is also up and running in case you just want to see the gauges in action. I guess I don’t have to mention that the Gauge is a fully customizable library that has many more settings than these two used in this example.

Now that I think about it, you could probably make this even better by making the id generation automatic. For example, using a pre-defined word with a static counter would be one way of doing this.

After all, isn’t it a bit against Vaadin ideology that the user has to care about things like DOM ids?


Tapio works at the Vaadin team helping our customers in building solutions on top of Vaadin Framework. Inspiration for this blog post originates from a support question.