A Skeleton for Custom Controls with WinJS

November 11th, 2012 11:13 AM ∙ Dibbs

If you’ve worked on or are starting to work on a Windows 8 app with relative complexity, you’ve probably encountered a scenario where you thought: “hey, instead of all this repeated markup, this might be better as a control.”

I’ve run into this myself a couple of times and have settled on a fairly solid skeleton using the WinJS libraries to assist where they make sense. Let’s walk through the bones of this gent together.

Introductions all around

I won’t start off at the beginner level here, I’m going to make some assumptions that you already have a basic understanding of how to go about using the WinJS libraries, and how vanilla JavaScript works at an intermediate level.

Below you can see the basic skeleton of a custom TurtlePower control.

(function () {
    "use strict";

    WinJS.Namespace.define("GotDibbs.Controls", (function createClass() {

        var TurtlePower,
            otherGlobals;

        TurtlePower = WinJS.Class.define(
        function constructor(element, options) {
            var control = this;

            if (!element) {
                throw "Element must be provided.";
            }
            options = options || {};

            control.element = element;
            control.options = options;

            control.createVisualTree();
        },
        (function defineInstanceMembers() {

            function setValue() {
            }

            function setDisabled(isDisabled) {
            }

            function createVisualTree() {
                var control = this,
                    elem = control.element;
            }

            return {
                setValue: setValue,
                setDisabled: setDisabled,
                buildControl: buildControl
            };

        })());

        return {
            TurtlePower: WinJS.Class.mix(TurtlePower, WinJS.Utilities.eventMixin)
        };

    })());

})();

As you can see, one of the big concepts I use with frequently is that of the revealing module pattern, created using immediately-executing functions. These patterns really help to keep your code clean and readable.

One brief step to the side for a second: you’ll notice that I name all of my immediately-executed functions. They would otherwise be anonymous and that can cause issues when you’re looking at a stack trace. Who wants to see a listing of “anonymous function > anonymous function > annonymous function”? How helpful is that?

I’m going to skip over the usage of the WinJS.Namespace.define and WinJS.Class.define functions’ core functionality, but you can visit the links inline to see a bit more detail on each.

Now let’s take a step closer and start to break down the control into some of it’s key components.

Stepping into the Constructor

Writing JavaScript using the WinJS libraries really makes you know your stuff when it comes to emulating OOP in JavaScript. If you notice above in the skeleton, we have a function called constructor which takes in a DOM element and an options object. This is where our control’s life begins. When we call WinJS.UI.processAll() in our app and it encounters one of our custom controls created declaratively in the HTML, our constructor function is what gets called to bring our control to life. Note that the function is also called when you instantiate the control programmatically via something a la:

var power = new GotDibbs.Controls.TurtlePower(elem, opts);

So what do we want to do here? Let’s break it down starting from the top.

function constructor(element, options) {

As we’ve already mentioned, the two parameters to this function are a DOM Element (usually a DIV already declared somewhere), and an Options object which allows us to change the behavior of our control from one instance to the next.

var control = this;

Here, if you’re not too familiar with JavaScript, you might get a bit lost, and if you’re not clear on what this line is doing exactly I’d suggest doing a bit of research on the JavaScript this keyword. Basically in the current context all we’re doing is “aliasing” the instance of the control to the variable control. I am a big proponent of “aliasing” the this keyword because it provides more insight as to what exactly you’re expecting this to be.

if (!element) {
    throw "Element must be provided.";
}
options = options || {};

control.element = element;
control.options = options;

In the above section we do a couple of things to make sure our control is being instantiated correctly, and then we cache a reference to both the element and the options that were passed into the function. No big surprises there.

control.buildControl();

The last line actually calls the control instance’s buildControl function which is defined in the instance members that follow the control’s constructor. Let’s head over there now and take a peek at the torso of our skeleton; where the real meat of our control will live.

Getting Fancy with Instance Members

Thanks to the revealing-module pattern, we can see from the defineInstanceMembers function that the three core public functions for this control are: setValue, setDisabled, and buildControl.

The purpose of the buildControl function should seem a bit self-explanatory, but it is basically what handles making sure our control contains the appropriate DOM elements, the appropriate styles, and makes sure the appropriate internal events are handled … well, internally. This function is exposed just so that our constructor can see it and call it. If this were C#, it’d probably actually be private, protected, or internal.

The setValue and setDisabled functions are there to be invoked on an instance of our control from JavaScript after the control’s instantiation. For example say we had another dependent control that when it was set to specific value, we wanted our TurtlePower control to be set to an appropriate value and then disabled. This would then look something like this:

var elem = document.querySelector("#turtlePower");
elem.winControl.setDisabled(true);
elem.winControl.setValue("NinjaFu");

You can see that basically we just invoke the functions directly off of the winControl object defined on the element. Pretty simple, right? Well now what if we need to know when something changes on our control? Let’s hop into that now.

Using Events (and not Data Binding)

Generally when the value of our control (assuming it’s an input control for this instance) is set through the UI or through code, we probably want to know so we can track it’s value through it’s lifetime. This can usually be done generally in one of two ways with WinJS: Binding or Events.

Sadly, I’ve run into about a bazillion problems with data binding in WinJS and therefore tend to avoid it now, especially with controls. Some brief examples:

So wait … if we can’t effectively use binding how ever will we know when a value changes? Well it’s over to the event-based model for now.

Instead of mixing our class with the WinJS.Binding.mixin, we mix it with WinJS.Utilities.eventMixin which automatically adds functions such as addEventListener and dispatchEvent to our class. We achieve this in the return statement at the end of our namespace:

return {
    TurtlePower: WinJS.Class.mix(TurtlePower, WinJS.Utilities.eventMixin)
};

What exactly does this do for us, you ask? This allows us to add a simple call to dispatchEvent to notify any listeners of that event. For example, in our setValue function we would call something like the below to notify listeners that the value has changed.

function setValue(val) {
    var control = this;
    // ... set the value on the control ...

    // Notify listeners
    control.dispatchEvent("change");
}

…and we listen for a change like this….

var elem = document.querySelector("#turtlePower");
elem.winControl.addEventListener("change", function (e) {
    // ... handle on change event ...
});

You might be thinking well let’s just switch to using Knockout.js or some similar library to achieve two-way binding … but it actually can get pretty messy going that route. However, it is an option nonetheless.

Conclusion

What we’ve achieved here is to create a skeleton of a WinJS control conforming to more common and modern web-based JavaScript standards. The patterns used within really help to promote readability – which is essential when creating any reusable and maintainable control. After all what good is converting frequently used code into a control if no one can read it or understand what it is doing? I’d be curious to here if anyone has any feedback and always welcome different approaches!

Categories: .NET, Development
Tags: ,
Comments: 1 Comment.

Comments
Pingback from Windows Store Developer Links – 2012-11-13 | Dan Rigby - November 12, 2012 at 6:50 pm

[...] an async dialog that allows for custom content and works more like the MessageDialog…”A Skeleton for Custom Controls with WinJS (GotDibbs)“If you’ve worked on or are starting to work on a Windows 8 app with relative [...]













Please leave these two fields as-is:

Protected by Invisible Defender. Showed 403 to 262,882 bad guys.