PAGES

9

Oct 12

Large Datasets in Windows 8: Part 1 – Displaying the Data



Screen shot

Note: This is part one of a two part series. Part two focuses on how to sync datasets to a local SQLite database and can be found here.

Windows 8 offers some great controls to display data. For this post, I’ll be focusing on the Win8 ListView control. The ListView gives you the ability to display items in a grid or a list with complete control of item templates. But first, let’s focus on getting data into the application.


Retrieving Data from a Web Service

For this example I am going to use the City of Chicago Data Portal, in particular, the Current Employees Names, Salaries, and Position Titles dataset. Even though the front end will be done in HTML and JavaScript, I recommend creating a C# data layer. While it is possible to call web services from JavaScript, I prefer moving this logic into C#. I have worked with both and found the C# data layer to be not only cleaner, but also easier to work with in general.

This dataset contains around 32,000 rows of information. Loading all that data into a ListView at once would bring performance to a grinding halt. So, we are going to page our data by calling the web service with a start value and a length value that determines how many records are returned. We see the main piece of this call in the below from RemoteDataProviderPartial.cs:

   
    public async Task<List<EmployeeSalary>> GetEmployeeSalariesAsync(
            string start, string length)
        {
            var response = await MakeHttpWebRequestAsync(
                RemoteDataOperationUrls.EmployeeSalary.EmployeeSalary_Get,
                "GET",
                null,
                new Dictionary<string, string>(){
                    {"ST", start},
                    {"LGTH", length}
                });
            return JsonHelper.Deserialize <List<EmployeeSalary>>(response);
        }

In this call I’m referencing the EmployeeSalary_Get url and deserializing the response into a List of EmployeeSalary objects. The full url that we are using is “https://data.cityofchicago.org/views/xzkq-xp2w/rows.json?method=getByIds&asHashes=true&start={ST}&length={LGTH}&meta=false”. You can put that url into Fiddler and see responses if you like, but be sure to replace the {ST} with a start value (or 0) and {LGTH} with how many records you would like returned. Now that we have data returned from the service, we need to tackle how we can make the above call asynchronously from javascript.


Displaying Data in a ListView

Without diving too far into the code, we initialize a sealed class (Locator.cs in the source code) in default.js that has static references to a few different properties. The only one that we are concerned about for now is the Application Model. In a more detailed application, the Application Model could be used to store current application data like active selections. For our simple app, we’ll just use it as an accessor for web service data through the call GetEmployeeSalariesRemoteAsync(int page).

By setting up our data layer this way, we can easily access the call through javascript. The following is a subsection of the home.js page:

    var app = Clarity.Samples.LargeDataset.Services.Locator.applicationModel;

    var onReady = function (element, options) {
        page = element;
        viewModel = jsBind.as(new homeViewModel());

        currentPage = 1;

        var promise = WinJS.Promise.wrap(app.getEmployeeSalariesRemoteAsync(parseFloat(currentPage)).then(employeesLoaded));
        promise = promise.done(function () {
		jsBind.processAll(page, viewModel);
                return markup.processAll(page);
        });
    };

    var employeesLoaded = function (employees) {
        viewModel.employees = new jsBind.List(employees.map(function (employee) { return new employeeViewModel(employee) }), { binding: true });
        var employeeList = document.querySelector("#employeesList").winControl;
        employeeList.itemDataSource = viewModel.employees.dataSource;
    };

The above code obviously won’t run if copy and pasted, but let’s step through it line by line. First we’re pointing the app variable at our C# Application Model namespace. In the onReady function, we’re creating our view model, setting our current page to 1, then calling our Application Model to get us the employee data from the web service. When that async call returns, we jump into the employeesLoaded function. In here we map each object in the list that was returned to a javascript employee view model and add it to our base home view model’s employee collection. We then get the ListView winControl by its HTML id and bind the employees to the ListView. The HTML for the ListView is shown below:

<div id="employeesList"
   data-win-control="WinJS.UI.ListView"
   data-win-options="{itemTemplate:select('#employeeTemplate'), layout:{type:WinJS.UI.GridLayout}, selectionMode:'none'}">
</div>

And the template:

<div id="employeeTemplate" data-win-control="WinJS.Binding.Template">
   <div class="tile employee">
      <header>
         <h2><span data-win-bind="textContent: name"></span></h2>
         <h3><span data-win-bind="textContent: jobTitle"></span></h3>
         <h3><span data-win-bind="textContent: department"></span></h3>
         <h3><span data-win-bind="textContent: salary"></span></h3>
      </header>
   </div>
</div>

As you saw before, the items of the ListView were bound in JavaScript. That means that each item is of type employeeViewModel which I skipped showing because of its simplicity. It simply contains the properties that are bound in the above template.

Since we only pulled back one page worth of data, I added some paging buttons to the bottom of the page. I won’t go through the implementation since its rather trivial, but whenever you click a button, it sends a page number to the Application Model. The Application Model has a constant telling it how many records to retrieve (12 is default) and calculates the start offset accordingly. For example, if the view sends “13″ down as the page number, the Application Model would contact the web service with a start value of 144 ((13-1) * 12 – remember you start at 0 for page 1) and a length of 12.

That’s all there is to it. If you’re interested in how to sync all the data from the web service into a SQLite database, you can check out part 2 here.

Continue to Part 2 – Syncing Datasets to SQLite


Source

Clarity.Samples.LargeDataset.zip

Note: The City of Chicago changed their variable names from last week to this week and I wouldn’t be surprised if they do it again. This may just happen quarterly when they update the data. If you run the project and a javascript error is thrown saying a value cannot be null, it just means the mapping needs to be redone. If you open Fiddler and get values back using the url above and open Clarity.Samples.LargeDataset.Services.RemoteData.ServiceModel.Model.cs you’ll see above the Name, JobTitle, Department, and Salary variables are name mappings. If you replaces those with the corresponding new numbers from Fiddler, everything should work just great again.

1 comment , permalink


Tagged , , ,