Sunday, April 1, 2012

Upshot.js & knockout.js: The HTML5 client for WCF RIA services

As Microsoft is leaving the Silverlight track and more and more focusing on HTML5 for cross browser support, solutions are coming for several existing frameworks. One of the things I’ve been looking at is the possibility to reuse your existing RIA services ,who you currently use for your Silverlight application, and port them to HTML5 enabled website.

The HTML client for ria services started as an jQuery plugin called Ria.js. Currently this project is hosted into the ASP.NET Single Page Application. This is a new project template for building single page applications. The advantage of these kind of application is that they run completely in the browser, this way it’s pretty easy to enable your application to be used offline.

The JS client framework used for communicating with the server is Upshot.js. This framework provides a rich context for the object used on the client side, this includes change tracking, paging, sorting, … More information of this subject can be found on the Denver Developers blog. If you prefer watching a live demo, Steve Sanderson gave one at TechDays Netherlands.

While searching on the net for some more information, I noticed that the most examples focus on the use of Web API for communication with the server. Next to the standard Web API provider for upshot, there is also support for RIA and OData. Since my post is handling RIA services, I‘ll be handling the riaDataProvider.

Building a live demo

Perquisite:

So lets get started by creating a new MVC4 project:

Create SPAWithRiaServices project

If we press the OK button, we can choose which MVC4 project template we want to use. In our case we choose for the Single Page Application.

Create SPAWithRiaServices project step 2

This template includes already some models, controls and views, but when we open up the scripts folder, we will see the script references for upshot.js, knockout.js, … Everything we need for building an HTML5 application with a rich context. But these frameworks evolve fast and thank god we have a thing called Nuget so we can easily update to the latest version. So this will be the next step in my tutorial, updating to the latest version of the single page application. Search for SinglePageAppliction in your Nuget packet management explorer or type the following in your package manager explorer:

Install-Package SinglePageApplication.CSharp



Once this is done, we need to add the reference to the ServiceModel so we can use the domainService class of the RIA services. The following references need to be added (note that these references come from the latest version of the WCF Ria Services SDK V1,  C:\Program Files (x86)\Microsoft SDKs\RIA Services\v1.0\Libraries\Server):





System.ServiceModel.DomainServices.Hosting
System.ServiceModel.DomainServices.Server



And one reference from the SP2 of the WCF Ria Services SDK V1, C:\Program Files (x86)\Microsoft SDKs\RIA Services\v1.0\Toolkit\Libraries\Server\SP2\





Microsoft.ServiceModel.DomainServices.Hosting



After adding the references, we need to configure our DomainServices. First thing we need to do is adding a new configuration section for the DomainService






<configSections>
  <sectionGroup name="system.serviceModel">
    <section name="domainServices" 
type="System.ServiceModel.DomainServices.Hosting.DomainServicesSection, 
      System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, 
      Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
      allowDefinition="MachineToApplication" requirePermission="false" />
  </sectionGroup>
</configSections>



Next we add a new httpModule for the DomainService to the system.web section






<system.web>
  <httpModules>
    <add name="DomainServiceModule" 
type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, 
      System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, 
      Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  </httpModules>
</system.web>



In the system.webServer section, we add a new module for the DomainService






<system.webServer>
  <validation validateIntegratedModeConfiguration="false" />
  <modules runAllManagedModulesForAllRequests="true">
    <add name="DomainServiceModule" preCondition="managedHandler"
 type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, 
       System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, 
       Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  </modules>
</system.webServer> 



As last we add the following system.serviceModel config section. Most important thing here is that we add a JSON endpoint, and on this endpoint we add the attribute transmitMetadata=”true” so the metadata is send to our HTML client.






<system.serviceModel>
  <domainServices>
    <endpoints>
      <add name="JSON" 
 type="Microsoft.ServiceModel.DomainServices.Hosting.JsonEndpointFactory, 
       Microsoft.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, 
       Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
       transmitMetadata="true" />
    </endpoints>
  </domainServices>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
        multipleSiteBindingsEnabled="true" />
</system.serviceModel>





Once all the references and configuration sections are added, we can start to code. The first thing we do is adding a new class and let it derive from DomainService.






using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using SPAWithRiaServices.Models;
 
namespace SPAWithRiaServices.Services
{
    /// <summary>
    /// Domain Service responsible for todo items.
    /// </summary>
    [EnableClientAccess]
    public class TodoItemDomainService : DomainService
    {
        [Query(IsDefault = true)]
        public IQueryable<TodoItem> GetTodoItems()
        {
            IList<TodoItem> todoItems = new List<TodoItem>();
 
            todoItems.Add(new TodoItem() 
                { TodoItemId = 1, Title = "Todo item 1", IsDone = false });
            todoItems.Add(new TodoItem() 
                { TodoItemId = 2, Title = "Todo item 2", IsDone = false });
            todoItems.Add(new TodoItem() 
                { TodoItemId = 3, Title = "Todo item 3", IsDone = false });
            todoItems.Add(new TodoItem() 
                { TodoItemId = 4, Title = "Todo item 4", IsDone = false });
 
            return todoItems.AsQueryable<TodoItem>();
        }
    }
}




The TodoItem Class looks as follows:






using System.ComponentModel.DataAnnotations;
 
namespace SPAWithRiaServices.Models
{
    public class TodoItem
    {
        [Key]
        public int TodoItemId { get; set; }
        [Required]
        public string Title { get; set; }
        public bool IsDone { get; set; }
    }
}



Now we can go to edit the views. The first view we need to change is the _Layout.cshtml in the shared folder. In here we delete the following line:






<script src="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Scripts/js")"></script>



And add the following references instead:






<script src="~/Scripts/jquery-1.6.2.js" 
        type="text/javascript"></script>
<script src="~/Scripts/modernizr-2.0.6-development-only.js" 
        type="text/javascript"></script>
<script src="~/Scripts/knockout-2.0.0.debug.js" 
        type="text/javascript"></script>
<script src="~/Scripts/upshot.js" 
        type="text/javascript"></script>
<script src="~/Scripts/upshot.compat.knockout.js" 
        type="text/javascript"></script>
<script src="~/Scripts/upshot.knockout.extensions.js" 
        type="text/javascript"></script>
<script src="~/Scripts/native.history.js" 
        type="text/javascript"></script>
<script src="~/Scripts/nav.js" 
        type="text/javascript"></script>


In the index.cshtml in the home folder, we add the following:



The configuration for using upshot






<script type='text/javascript'>
    upshot.dataSources = upshot.dataSources || {};
    
    upshot.dataSources.GetTodoItems = upshot.RemoteDataSource({
        providerParameters: { 
            url: "/SPAWithRiaServices-Services-TodoItemDomainService.svc", 
            operationName: "GetTodoItems" 
        },
        provider: upshot.riaDataProvider,
        bufferChanges: true,
        dataContext: undefined,
        mapping: {}
    });
</script>



The knockout view






<ol data-bind="foreach: todoItems">
    <li><strong data-bind="text: Title"></strong>
        <label>
            <input data-bind="checked: IsDone" type="checkbox" />
            Done
        </label>
    </li>
</ol>



And as last the ViewModel definition






<script type="text/javascript">
    function TodoItemViewModel() {
        // Data
        var self = this;
        self.dataSource = upshot.dataSources.GetTodoItems.refresh();
        self.localDataSource = upshot.LocalDataSource({ 
                                    source: self.dataSource
                                  , autoRefresh: true });
        self.todoItems = self.localDataSource.getEntities();
    }
 
    $(function () {
        ko.applyBindings(new TodoItemViewModel());
    });
</script>



When you run the application now, you should see the todo items appear on your screen with a little delay.



Conclusion



In your HTML applications you can take advantage of your existing RIA services with a minimum of effort. For now the possibilities are limited, but I think in the future we will be able to use all features as they are present in SL. By adding the transmitMetadata attribute to our JSON endpoint, it isn’t necessary to define the model again on the client side.

Wednesday, March 28, 2012

Developing metro apps (HTML & JS): My point of view

Last Friday I attended the first windows 8 developer day in Belgium. A great opportunity to learn a bit more about developing Metro apps. One of the demos given by Giorgio Sardo showed how easy a HTML 5 application could be copied and pasted into a Metro app without having to rework even a single line of code. At first sight this looks pretty amazing, but I got me thinking.
The first thing I noticed is the fact that the HTML 5 application was actually a game. That is one of the reasons it could be easily ported to a Metro app. If we would want to port a business app like an e-commerce site, that wouldn’t be so easy. One of the important issues when developing Metro apps is the design. The philosophy of Microsoft when designing an application is that all applications have a similar look & feel and should react on the same way.
Almost all websites are designed for a single resolution and that is in contrast with the Metro philosophy where all apps should optimize their viewport for every screen. This means you’ll have to redesign your application so it changes its content to fit to the screen. Also you should implement the capabilities to change the view depending on the position of your device (landscape or portrait mode) and the multitask options which allows you to run multiple applications side by side.
Next to the Metro design these Metro apps should at least have some specific windows 8 features like redefined search and sharing capabilities. It's only at this point you really start to take advantage of some of the new windows 8 features. But the most important thing you should implement is the live tile. This must engage the user to consume your application. You can do this by providing the user real-time information about your app.
A second consideration is the fact when you just copy and paste your web application, you don’t take advantage of all the possibilities present in WinRT. With WinRT you can make calls to the operating system. For example you can access the webcam, save a file to the file system; share data … Developing Metro apps with JavaScript also brings the WinJS namespace. In here we find the Promise object that enables an easy way for the developer to handle asynchronous calls. Because all calls that take more than 50ms are preformed asynchronous by default, this can come in handy when you are making WinRT calls.
After the theoretical part, it was time for the real work: an App-a-thon. Here we got the possibility to put all our theoretical knowledge into practice. Together with 4 RealDolmen colleagues (Maarten Balliauw, Xavier Decoster, Wesley Cabus and Angelo Trotta) we developed a metro Nuget Package Explorer. This allows the users to view NuGet repositories and their package details. All this information gets retrieved from the given feeds and gets stored in an IndexedDB for performance. The application is completely build in HTML5 and uses JavaScript for the logic.
Surprisingly, developing this application went pretty fast. I took some time to adjust to the fact your writing a client application with JavaScript, but all knowledge about JavaScript in the past, could be reused pretty easy. There was one thing we were struggling with. When working with the promises you have to be very careful to call objects in the UI-thread, otherwise you can get some strange exceptions.
Conclusion
After a whole day Windows 8, I really excited to start develop metro apps. Enabling developers to develop a metro app in HTML/JS was definitely a good choice Microsoft made. This way a whole new group of developers can start building Metro apps. But the business has to be aware that you can’t just copy and paste your web app in a Metro project and call it Metro app. Metro apps have a philosophy and that should be respected. Also it would be a shame if you wouldn’t take advantage of all the new features that Metro apps provide.
I’m looking forward to the next app-a-thon and hope that our team can come together again to win the contest this time. (Ended second last time.)
Currently I’m trying to port our Linq2IndexedDB project to use the WinJS promises instead of the jQuery promise. I hope to announce this feature in the near future.

Saturday, March 10, 2012

JavaScript: Change entered character in keypress event

Recently I came across a problem with a numeric input field. For globalization issues a decimal had to be separated by a comma instead of a dot. So there were 2 solutions in this case. I could prevent users from typing in the dot, or I could replace the dot by a comma when typed. I went for the second one, because on the numeric keypad you only have a dot. So this way it would be easier for the users to input the decimal. It also works the same as applications like Excel who change the dot to a comma to if your regional settings are set that way.

At first sight this looked like an easy chore. In most cases the keypress or keydown event gets handled and the keyCode of the dot gets blocked. Instead the comma is added at the end of the current value of the input field.

   1: // All decimal input fields have a class named 'number'
   2: $('input.number').each(function (){
   3:     $(this).keypress(function(e){
   4:         // '46' is the keyCode for '.'
   5:         if(e.keyCode == '46' || e.charCode == '46'){ 
   6:             //Cancel the keypress
   7:             e.preventDefault(); 
   8:             // Add the comma to the value of the input field
   9:             $(this).val($this.val() + ',');
  10:         }
  11:     });
  12: });
  13:  

In most cases this will do, but when users start editing the decimal, troubles arrive. When the user types a dot inside the number, the comma will be added at the end of the number. But this isn’t what the user wants. He wants to have the comma placed on the location where he type the dot. After some googling, I found the following solution:



   1: // All decimal input fields have a class named 'number'
   2: $('input.number').each(function () {
   3:     $(this).keypress(function(e){
   4:         // '46' is the keyCode for '.'
   5:         if(e.keyCode == '46' || e.charCode == '46'){
   6:           // IE
   7:           if(document.selection){
   8:                 // Determines the selected text. If no text selected,
   9:                 // the location of the cursor in the text is returned
  10:                 var range = document.selection.createRange();
  11:                 // Place the comma on the location of the selection,
  12:                 // and remove the data in the selection
  13:                 range.text = ',';
  14:           // Chrome + FF
  15:           }else if(this.selectionStart || this.selectionStart == '0'){
  16:                 // Determines the start and end of the selection.
  17:                 // If no text selected, they are the same and
  18:                 // the location of the cursor in the text is returned
  19:                 // Don't make it a jQuery obj, because selectionStart 
  20:                 // and selectionEnd isn't known.
  21:                 var start = this.selectionStart;
  22:                 var end = this.selectionEnd;
  23:                 // Place the comma on the location of the selection,
  24:                 // and remove the data in the selection
  25:                 $(this).val($(this).val().substring(0, start) + ','
  26:                  + $(this).val().substring(end, $(this).val().length));
  27:                 // Set the cursor back at the correct location in 
  28:                 // the text
  29:                 this.selectionStart = start + 1;
  30:                 this.selectionEnd = start +1;
  31:             }else{
  32:                 // if no selection could be determined, 
  33:                 // place the comma at the end.
  34:                 $(this).val($(this).val() + ',');             
  35:             }
  36:             return false;
  37:         }
  38:     });
  39: });



What we do is use the provided functionalities in the browsers for detecting selections in an input field. If no text is selected, the range will return the location of the cursor inside the input field. This way we can provide the correct implementation. So even when text is selected and the dot key is pressed, the selected text will be replaced by the comma.

Thursday, February 23, 2012

LINQ to Indexed DB

Since I started experimenting with the Indexed DB API, I have been searching for a simple way to add, retrieve, change, … data. In the beginning I wrote a little framework that provided some methods to retrieve, add, … data without having to think how to setup a connection. For experimenting this was enough, but when I started to build some demo apps, I noticed a needed a more generic way to do my CRUD operations.

So I start searching the internet for frameworks around the Indexed DB API’s. One project I found very interesting. It was the one of nparashuram. He was started with a Linq 2 Indexed DB Framework. Everything was still very basic, but it was well structured. Also it was only compatible with Firefox and Chrome, but not with the IE prototype and IE 10. So I contacted nparashuram and we decided to keep working on it together.

The framework is based on promises. This way we can easily handle the async calls which the Indexed DB API uses. We also use it as return value of the query you make. This way you can easily decide whether you want to use the complete method or the on progress method when retrieving multiple records. Also I believe this will be a programming model which we will see more and more in the future. Certainly if we want responsive applications.

The framework takes away the complexity of opening a database, creating transactions, … the only thing you need to worry about is how to structure your database and how to query. It also provides a way of creating the database structure while querying. This way you don’t have to create object stores or define the structure. Just inserting data to an object store, the object store will be created if not present.

The code of the framework can be found on codeplex and works as an extension on the jQuery framework. If you have some feature request you can add them here. Or you can contact me if you have some questions about it.

Keep following my blog or codeplex for new features and samples on this framework.

Friday, February 17, 2012

Web worker: running js tasks in the background

Since JavaScript is more and more used for building applications, rather than providing extra features, you need to take care you don’t freeze the UI. This is one of the reasons why the W3C introduced the web worker API. This API provides a way to run JavaScript in a thread different form the UI thread. This way long running code such as sorting large array’s, won’t freeze the the UI or make your application unresponsive. Generally, workers are expected to be long-lived, have a high startup performance cost and a high per-instance memory cost.

workers

When we want to run code in a thread different from the UI thread, then we need to put this code in separate file. This file, will then be called by the worker to run in another thread. The only thing you need to do in the in the code that will run in another thread is calling postMessage method whenever you want to send data to the UI-thread.

Continuous worker

This code will start running when ever a worker with that file is instantiated and will continue running.

Example of a file (prime.js) to run in the background:

   1: var n = 1;
   2: prime: while(true){
   3:     n += 1;
   4:     for (var i = 2; i <= Math.sqrt(n); i += 1){
   5:         if (n % i == 0){
   6:             continue prime;
   7:         }
   8:     }
   9:     // Found prime
  10:     postMessage(n)
  11: }

This code will send a message to the UI thread every time a prime is found.



   1: // Starts a new worker
   2: var worker = new Worker('prime.js');
   3: worker.onmessage = function (event){
   4:     // event.data contains a prime
   5:     document.getElementById("result").textContent = event.data
   6: }

The code above is used to create a new worker. The worker will execute the code in the prime.js file in a thread separate from the UI. In our case, the code will start calculating which numbers are primes.


The onmessage event on the worker gets triggered every time the postMessage method is called inside the worker thread. This way we can display the prime on the screen.


dedicated Worker


This worker will only start executing when you call the postMessage method on the worker.


Example of a file (sort.js) to run in the background:



   1: onmessage = function (event) {
   2:     var data = event.data.data;
   3:     var propertyName = event.data.propertyName;
   4:     var sortedData = data.sort(JSONComparer(propertyName).sort);
   5:     postMessage(sortedData);
   6:     return;
   7: };
   8:  
   9: function JSONComparer(propertyName) {
  10:     return {
  11:         sort: function (valueX, valueY) {
  12:                 return ((valueX[propertyName] == valueY[propertyName]) 
  13:                         ? 0 : ((valueX[propertyName] > valueY[propertyName]) ? 1 : -1));
  14:         }
  15:     }
  16: }

The onmessage method will handle a postMessage send to the worker. This will start the work you want to execute. In this case the code will sort an array of JSON objects on a given property. When the array and the propertyName is provided as data in the postMessage method, The data will get sorted on the property with the given propertyName. When the sort is completed, the sorted array will be send to the UI.



   1: var worker = new Worker("sort.js");
   2: var data = [{ name: "test2" }, [{ name: "test4" }, [{ name: "test1" }, [{ name: "test3" }]
   3: worker.onmessage = function (event) { /* event.data contains the sorted Array */) };
   4: worker.postMessage({ data: data, propertyName: "name"});

With the postMessage method we can provide the data that is needed to sort an array. When the sorting is completed and the postMessage method in the background thread is called, the onmessage function will be triggered in the UI thread so we can process the sorted array.


More information about workers can be found here.

Saturday, February 4, 2012

Indexed DB: To provide a key or not to provide a key

I am currently busy writing a little framework around the Indexed DB API to make it easier to use Indexed DB. One of the things I was struggling with was when the key parameter must or mustn’t be provided. So I tested every possible combinations.

For this I created 4 object stores with different combinations:

  • Object store 1: KeyPath: “Id”, autoIncrement = true
  • Object store 2: KeyPath: undefined, autoIncrement = true
  • Object store 3: KeyPath: “Id”, autoIncrement = false
  • Object store 4: KeyPath: undefined, autoIncrement = false

Next, I tried adding four different combinations, and here are the results.

Add/Put parameters

Object store configuration

Value

Key

Object store 1

Object store 2

Object store 3

Object store 4

{ Name: "test" } undefined successful successful failed failed
{ Id: 1, Name: "test" } undefined successful successful successful failed
{ Name: "test" } 1 failed successful failed successful
{ Id: 1, Name: "test" } 1 failed successful failed successful

 

Conclusion

  1. When a KeyPath is defined, the key parameter must be undefined
  2. When no KeyPath is defined and there is no autoIncrement, a key must be provided
  3. When no KeyPath is defined and there is autoIncrement, a key can be provided
  4. When a KeyPath is defined and there is no autoIncrement, an attribute with the name of the KeyPath must be present.