Introduction
Lately, I was involved in a few Windows 8 projects. One of the questions that I am being asked from time to time is how to write your own Windows Store app custom control
with the WinJS library. This article will try to give you a head start for your WinJS custom control library. During the article you will build an autocomplete control
and learn a few WinJS concepts on the way. So let's get started.
The WinJS.Namespace object
The first thing to do when you write your own controls is to put them inside their own namespace. Namespaces help to group a set of identifiers into a logical group.
Those identifiers can be classes, functions, or any other development language structures that you wish to group. For more details about how to create JavaScript namespaces,
go to the following link.
WinJS includes a Namespace
object that can help you define namespaces. You will use the
WinJS.Namespace.define
function which receives two parameters: the namespace
name and an object populated with the namespace’s identifiers. The following example defines a new namespace called
MyApp.UI
:
WinJS.Namespace.define("MyApp.UI", {
});
The WinJS.Namespace
object also includes a defineWithParent
function to define namespace hierarchies but in this example I don’t use it.
Defining the control class
So now we have a namespace, what's next? Creating a control class.
Since JavaScript doesn’t include a class concept you can mimic that behavior using constructor
functions and prototypical inheritance. Luckily, WinJS includes a Class
object that exposes functions to define, derive, or mix JavaScript “classes”.
In order to define a class, you will use the WinJS.Class.define
function. The function receives three parameters: a constructor function to initialize the objects,
an instance member object to add to every created instance, and a static member object which will be added to the prototype of the class. Let's define
the Autocomplete
class inside the previously created namespace:
WinJS.Namespace.define("MyApp.UI", {
Autocomplete: WinJS.Class.define(function (element, options) {
},
{
{
});
As you can see, the define
function gets all the three parameters. In the constructor function you will get two parameters which are the elements for the control
and an options
object to configure the control. The two parameters are mandatory for building a WinJS control since they are mapped
to the data-win-control
and data-win-options
HTML attributes of the element when the
processAll
function is called.
Adding control functionality
Now that we have our template for the control class, let's create its functionality. Before we write any code, let's understand what we expect from the control.
The control needs to create on the fly a datalist
element and attach it to an input type (if you want to read about the new HTML5
datalist
element,
you can go to the following link).
The control also needs to get as input an option list that will be shown as the autocomplete list. So let's write the code:
(function (WinJS) {
WinJS.Namespace.define("MyApp.UI", {
Autocomplete: WinJS.Class.define(function (element, options) {
if (!element || element.tagName.toLowerCase() !== "input")
throw "input type must be provided";
options = options || {};
this._setElement(element);
this._setOptionList(options.optionList);
this._element.winControl = this;
WinJS.UI.setOptions(this, options);
this._createDataList();
},
{
_element: null,
_optionList: null,
_setElement: function (element) {
this._element = element;
},
_setOptionList: function (optionList) {
optionList = optionList || [];
this._optionList = optionList;
},
_createDataList: function () {
var i = 0,
len = this._optionList.length,
dl = document.createElement('datalist');
dl.id = 'dl' + this._element.id;
this._element.setAttribute("list", dl.id);
for (; i < len; i += 1) {
var option = document.createElement('option');
option.value = this._optionList[i];
dl.appendChild(option);
}
document.body.appendChild(dl);
},
element: {
get: function () {
return this._element;
}
}
})
});
}(WinJS));
As you can see, the constructor function first checks whether the element is an input type. It also sets the element and the option list
which is supposed to be supplied in the option object. The last thing it does is to create the
datalist
element. In the instance member object
you divide the functions and properties to private and public member sections. This division is only logical (and marked with comments) and as you can see, private
members get the underscore character before their name. The main functionality here is the
_createDataList
function which handles the creation
of the datalist
and sets the input type list attribute to the list
ID. All the other functions are straightforward.
Using the control
Now that we have the control, let's use it. In the example app, I added a data.js file under the js
folder and wrote the following code:
(function () {
"use strict";
WinJS.Namespace.define("Data", {
cities: ["Seattle", "Las Vegas", "New York", "Salt lake City"]
});
})();
I could get any data from any other data source (cloud, service, storage) but I wanted the example to be as simple as possible.
In the home.html file, located in the pages/home folder, I added a text input type which is configured using the
dat
a-win-control
and data-win-options
attributes:
<input type="text" name="txtCities" id="txtCities"
data-win-control="WinJS.UI.Autocomplete"
data-win-options="{ optionList: Data.cities }"/>
Don’t forget to add the script tags for the autocomplete.js file and data.js file or else the example won't work.
Now everything is set and you can run the app and see the result:
Summary
WinJS comes with a lot of built-in controls such as the FlipView
,
ListView
, and ToggleSwitch
. On the other hand, sometimes it is necessary to build your own custom controls
to add your own functionality. In this article you learned the basics of creating your own WinJS custom control. You started with a namespace and finished with an autocomplete control.
By using the same technique, you will be able to create your own controls and incorporate them in your Windows Store apps.