Windows 8 App Development - Bindings, Collections, Templates

Here is the next post about Windows 8 Application Development with JavaScript, HTML5 and CSS3. In the last post I said that I will talk about Bindings, Collections and Templates. 
As in my previous posts I will present an application, you can access it's source code along with the older codes from previous blog posts. The current project is named: HouseRental_Improved.

In this project I wanted to simulate a more realistic scenario. If you remember in my older versions of the HouseRental app I had 4 houses for sale and that's it. These days the business is getting "alive" :), this means I have more houses for sale, there are requests from owners who want to sell their houses and they want to include their properties in my list of offers. This meant I had to rethink the architecture of the application. I thought that every computer which will run Windows 8 will have an Internet connection, otherwise I don't really understand why to use Windows 8. So by having an Internet connection my application can connect to a server application somewhere on the web and update the list of houses which are for sale.



The Server application


In the solution, hosted on GitHub, I have 2 projects. One application (HouseRental_Backend) is the server application, this is an ASP.NET WebApi application. The code for this application is very simple. Besides the generated code, I added a new ApiController (see HouseController.cs file) which has some sample data generating mechanism and a public method which responds to requests by sending data in JSON format.
The controller uses a static list of type House. This list is populated when the static constructor of the ApiController is invoked and the GenerateRandomData() method is invoked which mixes sample data from predefined values. 



The HouseRental_Improved application

The current version of the application looks like this:

HouseRental_Improved Application - Main Screen
HouseRental_Improved Application - Main Screen

As you can see the design of the application has changed a little. There is a picture of each house and at the bottom of the image there is a blue stripe and some information written on it (the city where the house is and the address). I will not explain the CSS part now, by checking out the CSS code I am sure you can understand it without any problem. 


The HTML5 story...

The app still has only one HTML page, the default.html. In the head section of the page the references for the new JavaScript files are added.

<head>
    <meta charset="utf-8" />
    <title>HouseRental_Improved</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
    <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

    <script src="js/house.js"></script>
    <script src="js/viewModel.js"></script>
    <!-- HouseRental_Improved references -->
    <link href="/css/default.css" rel="stylesheet" />
    <script src="/js/default.js"></script>
</head>


The body region has completely changed since the last version. Then it had one big div element inside with 4 div elements each containing information about a house for sale. In this version there are 2 div elements, one for a ListView control and one for it's template

Lets take a look at the code of the template first.

    <!-- TEMPLATE FOR LIST VIEW -->
    <div id="lstHouseTemplate" data-win-control="WinJS.Binding.Template">
        <div class="house">
            <img src="#" data-win-bind="src: ImageSource" />
            <div class="detail">
                <h5 data-win-bind="innerText:Info"></h5>
            </div>
        </div>
    </div>


We can see that the data-win-control attribute is set for a div element and this has the WinJS.Binding.Template value. This attribute helps the WinJS framework to identify this element as a template. The template contains another div element which on his own contains an image element and another div element. The div elements are used so often to obtain the wanted design. I wrote about the data-win-bind attribute in my previous blog post, so I will not explain this now.

The code for the ListView is more interesting and has a lot of similarities with code written for WPF/XAML apps.

    <!-- LIST VIEW -->
    <div id="lstHouse" data-win-control="WinJS.UI.ListView"
        data-win-options="{
        itemDataSource : HouseRental.ApplicationData.data.dataSource, 
        itemTemplate : lstHouseTemplate,
        onselectionchanged : HouseRental.Handlers.houseSelectionChanged }">
    </div>


Here, the data-win-control attributes appears too, but now this has the value of WinJS.UI.ListView. Another interesting attribute appears, the data-win-options. This attribute/property can have property:value pairs in the same way as the data-win-bind attribute has, the only important thing to note is, that the properties are from the control specified in the data-win-control attribute. In this specific case the control is a ListView, which has an itemDataSource property, an itemTemplate property and an onselectionchanged event.
The itemDataSource property will have the value of the HouseRental.ApplicationData.data.dataSource property (we'll see this defined in The JavaScript story... part below). By setting the itemTemplate property to an id of an existing WinJS.Binding.Template element, at runtime the WinJS framework looks for a control-template with the given id (in this case lstHouseTemplate) and applies this template to the data stored in the itemDataSource property of the ListView.
The onselectionchanged event occurs when the selection changes in the list, this is quite obvious. If you don't have a touch input (sadly I don't have one), you can select an element from a ListView by right clicking on the element. The selected element will get a nice border and will be marked with a tick in the upper right corner, check the red marked region in the following screenshot.

Selected elements in WinJS ListView
Selected elements in WinJS ListView

I wrote that the HTML code for WinJS apps has similarities with WPF/XAML code. You can see that everything can be set in a declarative way, the same way as it can be done in XAML. The syntax of declarations is very similar also. I think if a developer has worked with XAML and has a view formed to read efficiently XAML, he will not have any problem to read the HTML files which appear in Windows 8 Apps. I think this is true vice versa also.


The JavaScript story...

The JS code has even more changes then the HTML code had. The idea was to keep the MVVM design pattern, so first of all I needed a model on the client side. 

The house.js file creates a WinJS object with observable properties. If you look at the code, you'll see that I defined in the HouseRental.Entities namespace a new function/object House : function(model) which returns the WinJS observable object. The model passed to this function will be the object retrieved from the WebApi application's ApiController - HouseController through the GetHouses() method. 

The viewModel.js file is more complex. This contains 2 definitions. The first one is the definition for the ViewModel object inside the HouseRental namespace. This ViewModel object contains an object of type  WinJS.Binding.List, this will store the data received from the WebApi project. The function ViewModel returns an observable object. 

 WinJS.Namespace.define("HouseRental", {
        ViewModel: function () {
            var that = this;

            //declare an observable collection
            //this will store the HouseRental.Entities.House objects
            var _houses = new WinJS.Binding.List();

            //return an observable object
            return WinJS.Binding.as({

                //this property will be used in bindings
                data: _houses,

                addHouse: function (house) {
                    _houses.push(house);
                }
            });
        }
    });


The second definition is a kind of wrapper for the ViewModel object. 

    //this will help to build up the VM and load data
    WinJS.Namespace.define("HouseRental", {
        ApplicationData: WinJS.Binding.as((function () {
            var vm = new HouseRental.ViewModel();

            //get the data from the WEB API application
            WinJS.xhr({ url: "http://localhost:1053/api/house/GetHouses" })
                 .done(function (result) {

                     var jsonHouses = JSON.parse(result.response);

                     if (jsonHouses) {
                         for (var i = 0; i < jsonHouses.length; ++i) {
                             var newHouse = new HouseRental.Entities.House(jsonHouses[i]);
                             vm.addHouse(newHouse);
                         }
                     }
                 });

            return vm;
        })())


I needed this ApplicationData, to create an instance of the ViewModel type, and populate it with some data. A new instance of HouseRental.ViewModel is created and this object is returned. The WinJS.xhr(...) method gives the possibility to access other services, links...etc on the web. The .xhr(...) method makes an asynchronous call towards the GetHouses() WebApi method. We don't know when the response will come, so we have to use the .done(...)  callback to run some code after the response arrived from the WebApi . These type of callback functions are called promises in the terminology of WinJS. The  .done(...) method expects a function with one parameter as parameter: this will contain the response object from the accessed service. In this case the response contains data serialized in JSON format, so I used the JSON.parse() method to create objects from the response. After that I cycle through the objects and create objects of type HouseRental.Entities.House and add them to the vm object.


The code in default.js file also suffered some modifications. I added 2 event handlers to 2 events of the WinJS.UI.ListView object. One for the iteminvoked event - this occurs when the user taps (clicks) an element from the list; the selectionchanged event (NOTE: when assigning handlers in a declarative way the name of the events are prefixed with the on prefix; in this case onselectionchanged) occures when the user selects(right clicks) and element from the ListView.

 function initApp() {

        lstHouse.addEventListener("iteminvoked", function (args) {
            var currentItemIndex = args.detail.itemIndex;
            showMessage("The item is on the index: " + currentItemIndex);
        });
    }


I use the addEventListener(...) method of the lstHouse object - declared in HTML, has the data-win-control attribute set to WinJS.UI.ListView.

The only thing I do in both handlers is, that I construct a message for the user and display it with the help of showMessage() function. The MessageDialog object is the alter-ego of the well known  MessageBox from WinForms or WPF.

  function showMessage(message) {
        var msgBox = Windows.UI.Popups.MessageDialog(message);
        msgBox.showAsync();
    }


In order to use a handler in a declarative way there are some rules to respect. The first is, that you need to define the handler inside a namespace. The second rule is to mark the handler supported for processing. After this code executes, we can use the HouseRental.Handlers.house SelectionChanged function as a handler for the onselectionchanged event inside the data-win-options attribute.

    //define a method which can be used to assign handlers declaratively to events of the Windows 8 controls
    WinJS.Namespace.define("HouseRental.Handlers", {
        houseSelectionChanged: WinJS.Utilities.markSupportedForProcessing(function lstHouse_selectionChanged(eventInfo) {

            var selectedElements =
                lstHouse.winControl.selection.getItems().then(function (arg) {
                    showMessage(arg.length + " elements are selected.");
                });
        })
    });



This was the fifth post about developing apps under Windows 8 with WinJS framework, HTML5 and CSS3. As you can see we are making bigger and bigger steps with each blog post so I thought that...It would be great to hear from you, share your thoughts, what would you like to continue with, what else are you expecting from a series of post like these are...

The written JavaScript code is not bulletproof, in the house.js file, when accessing the properties of the model object it would be better to verify if the model object has the requested properties and only if it has them use those properties. When making the xhr(...) call towards the WebApi GET method - GetHouses() - the error handling is missing, if something goes wrong the application will crash.

It is important to mention that the class House from WebApi and the type from house.js contain the same property names; If  the names would defer changes would be necessary in the house.js file to adopt the initialization of the objects from the parameter model.



No comments:

Post a Comment