Controls in code

The use of the data- attributes and processAll to add custom controls to the HTML markup is a useful way to create a more advanced UI and it extends HTML to have some of the characteristics of a more advanced markup language like XAML. However, you don't have to make use of any markup in an HTML5 app. All controls, native or custom, can be created in code.

For example, to add a Rating control in code all you have to do is make use of the specified constructor:

var object=new WinJS.UI.Rating( element,options);

where element is the HTML element to be used to display the control and options specify the initial settings for the properties, if any in the same form as the JSON object used in the HTML markup.

For example, to add a Rating control at the position of a <div> we simply need to add a tag with an id that we can use to retrieve the element:

<div id="rating1"></div>

In the JavaScript program we now simply wait for the DOM to be loaded and retrieve the DOM object corresponding to "rating1". Next we simply use the constructor to build the control at the location of the <div>:

If you run this program you will see the default Rating control appear at the location of the <div>.

Notice that we no longer need processAll because this simply reads the DOM looking for data- tags and then does the same job of creating the control as we have just done explicitly using the constructor. Notice also that when we use the constructor we get the JavaScript object associated with the control automatically and the DOM object as part of the getElementById. If you create a control using HTML and processAll then the DOM object is stored in a global variable with the same name as the id of the tag used to create the JavaScript object.

You can use the second parameter in the constructor to set initial property values using the same format as in the data- attribute. For example, to set the maxRating and userRating properties you would use:

Of course, as you have the JavaScript control object, you can set properties directly in the usual way. However, there is a complication in that some variables cannot be changed after the control has been created. For example, you cannot set maxRating but you can set userRating:

rating.userRating = 4;

This changes the number of stars displayed.

At this point you may be wondering how setting a property to a new value can trigger the update of the control?

The answer is that modern JavaScript has getters and setters.

That is, if you define a setter function, assigning a value to a property doesn't store the value - it calls the setter. In this case the setter function not only stores the new value, it also updates the control.

If you look at the code in the JavaScript library you will discover that it says something like:

the set function is called which checks that the value is valid, stores the value in an internal variable, and then updates the control. Notice the convention that internal variables begin with an underscore.

Similarly, a getter results in a function being called when you retrieve a value.

The WinRT JavaScript libraries make a lot of use of getters and setters to check input values and to trigger updates. If you aren't familiar with getters and setters then look them up.

Of course, the rule that some properties have to be set by the constructor and cannot be changed subsequently, is one that is enforced by the way the setter for that property is written, i.e. Microsoft decides when a property would cause too much updating to be set after construction.

Code access to a markup control

There is one final aspect of working with a control in code that we need to cover. Suppose you have defined a control in HTML using the data- attribute. How do you work with it in code?

The answer is very easy.

When you processAll creates any WinJS controls it also adds a global variable with the same name as the id of the element used to create the control to the JavaScript. This global variable holds a reference to the DOM object.

Notice that WinJS does this for all of the HTML tags that you assign ids to so providing you with direct access to all of the DOM objects that make up the page.

WinJS also extends all DOM elements with a winControl property that returns any JavaScript controls that are bound to them.

If you run this program you should see four stars. Notice that the DOM element corresponding tot he control is stored in rating1 because this is the id of the div that we used to create the control in HTML.

You can of course do this as a two step process not using the global variable - first return the DOM object using getElementById and then return the JavaScript object corresponding to the control but the global variable and winControl approach is simpler.

Promises

There is a problem in that the previous code isn't quite correct. The problem is that processAll is asynchronous and it returns immediately even if it hasn't finished processing controls. What this means is that you shouldn't start setting properties on controls that might not have been created yet.

The proper way to do the job is to make use of the Promise object that the processAll function returns. This has a then method that accepts three functions: one that is run when complete; one that is run if there is an error; and one that is run as the asynchronous operation proceeds, i.e. a progress function.

So to do the job properly all we have to do is put the code that does the update into the first function specified in the then method:

For simplicity, the error and progress functions haven't been defined. In a real life program at least the error handler should be defined.

As you make more and more use of the WinRT JavaScript libraries you will become increasingly familiar with the idea of Promise objects and how to make use of them as part of the everyday asynchronous call.

Promise objects are covered in a later chapter.

Events

One of the common things that you have to do in code, no matter whether the control is created in code or in markup, is to attach an event handler.

Each custom control supports a range of events and the procedure for dealing with them is the same in all cases.

First, you need the JavaScript object corresponding to the control. You can either get this from the constructor if the control is created in code, or via winControl if it is created in markup.

Next, you simply use the addEventListener method to add the event handler.

For example, the Ratings control has a change event which is fired when the user tries to adjust a rating. To hook up an event handler all you have to do is:

Notice that in this case the name of the event is onchange. The rule is that if a property starts with "on" then its value is assumed to be a function. In this case it is assumed that myChange is a global function defined in the default.js file or elsewhere.

Summery

You can define WinJS objects using HTML markup with data-attributes or in code using a constructor function.

The processAll function converts the HTML tags with data-attributes into WindJS controls.

processAll also adds global variables with the same names as each DOM element id and referencing the corresponding DOM element.

Each control has a DOM object and a JavaScript object associated with it.

Control options are set using JSON notation either in markup or as the second parameter of the constructor.

You can modify some control properties via the JavaScript object's properties

DOM elements have been extended with a winControl property which references the associated JavaScript object.

You can attach events using the addEventListener method provided by the JavaScript object.

Where next?

In this article you have seen the basic ideas that are involved in using the custom WinJS controls. The example control, i.e. Ratings, is fairly typical of the simple controls, but things do get a little more complicated when you start to work with layout controls and more complex list controls. In particular there is the way databinding works to find out about.

You can see a full list of the custom controls in the listing for the WinJS.UI name space which is currently at