Mobile Zone is brought to you in partnership with:

Jorge is the author of three software development books: "Building a Sencha Touch Application", "How to Build a jQuery Mobile Application", and the "Ext JS 3.0 Cookbook". He runs a software development and developer education shop that focuses on mobile, web and desktop technologies. Jorge is a DZone MVB and is not an employee of DZone and has posted 52 posts at DZone. You can read more from them at their website. View Full User Profile

How to Create an ASP.NET Handler for a Sencha Touch Store

05.07.2013
| 3376 views |
  • submit to reddit

In this tutorial you will learn how to create a .NET server-side handler for a Sencha Touch store. Let’s begin by defining a sample Sencha Touch application with a “Hotels” store that contains data about a number of fictitious hotels. Our goal is to build the server-side .NET code that will work with the store in order to read, create, update and delete hotels information saved in a database.

To start, you will need to create a Visual Studio or WebMatrix project with the following directories:

app directories

The index.html file of the Sencha Touch app will look like this:

stores-5-index-file

Note that I placed the sencha-touch.css and sencha-touch-debug.js files outside the application’s root directory because I have multiple sample projects that reference these files. You can place the Sencha Touch files in any directory as long as you update their links in the index.html file.

Defining the Model and Store

In the model/Hotel.js file, you will define a Hotel model like so:

Ext.define('App.model.Hotel', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            { name: 'id', type: 'int' },
            { name: 'name', type: 'string' },
            { name: 'address', type: 'string' },
            { name: 'status', type: 'int' }
        ]
    }
});

You will define the Hotels store in the model/Hotels.js file:

Ext.define('App.store.Hotels', {
    extend: 'Ext.data.Store',
    config: {
        model: 'App.model.Hotel',
        autoSync: false,
        proxy: {
            type: 'ajax',
            api: {
                create: '../../services/hotels.ashx?act=createhotels',
                read: '../../services/hotels.ashx?act=loadhotels',
                update: '../../services/hotels.ashx?act=updatehotels',
                destroy: '../../services/hotels.ashx?act=erasehotels'
            },
            reader: {
                rootProperty: 'hotels'
            }
         }
    }
});

Pay attention to the way you specify the store’s proxy. The proxy is of type ajax, and its apiobject specifies the server endpoints for each of the proxy operations: create, read, update and destroy. Each endpoint has its own URL, with an “act” query string parameter that defines the action to be executed by the server.

Creating the .NET Handler for the Store

Now you will take care of the server-side code that will feed the Sencha Touch store. Create a services directory in your project, and add a Hotel.cs file to it. In the Hotel.cs file, define the Hotel class like so:

public class Hotel
{
    public string id { get; set; }
    public string name { get; set; }
    public string address { get; set; }
    public int status { get; set; }
}

Instances of the Hotel class will function as data transfer objects that will allow you to save and retrieve hotels data from the database.

Next, create a services/Hotels.ashx file. This will be your .Net handler.

hotels asp.net handler

Since the handler will send and receive json-formatted data, you will need a way to serialize and deserialize this data into instances of the Hotel class. One way to accomplish this is using the Json.Net library. Once you download the library, you will need to add a reference to it in your .Net project.

Your first step in the Hotels.ashx.cs file will be to modify the ProcessRequest function like so:

public class Hotels : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        string hotelJson;
        Hotel hotel = null;
        Hotel[] hotels = null;
        string result = "{\"success\":false}";
        string action = context.Request.QueryString["act"];

        if (action != null)
        {
            switch (action.ToLower())
            {
                case "loadhotels":

                    break;

                case "createhotels":

                    break;

                case "updatehotels":

                    break;

                case "erasehotels":

                    break;
            }
        }

        context.Response.ContentType = "application/json";
        context.Response.Write(result);
    }

	public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

Inside ProcessRequest, you capture the request sent from the Hotels store through the actquery string parameter, and save it into the action variable. The switch statement helps you execute the requested action.

Loading Records

To send a batch of hotels data to the Sencha Touch store upon receiving a loadhotelsrequest, you will add the following code:

case "loadhotels":

    // Here you would retrieve the hotels from the database.
    // Returning a hard-coded hotel for demo purposes.
    hotels = new Hotel[4];

    Hotel hotel1 = new Hotel() { id = "1", name = "Siesta by the Ocean", address = "1 Ocean Front, Happy Island", status = 1 };
    hotels[0] = hotel1;
    Hotel hotel2 = new Hotel() { id = "2", name = "Gulfwind", address = "25 Ocean Front, Happy Island", status = 0 };
    hotels[1] = hotel2;
    Hotel hotel3 = new Hotel() { id = "3", name = "South Pole View", address = "1 Southernmost Point, Antarctica", status = 1 };
    hotels[2] = hotel3;
    Hotel hotel4 = new Hotel() { id = "4", name = "North Pole View", address = "1 Northernmost Point, Artic Ocean", status = 0 };
    hotels[3] = hotel4;

    result = "{\"success\":true,\"hotels\":" + JsonConvert.SerializeObject(hotels) + "}";

    break;

Note that for demo purposes, you are hard-coding the hotels data. In a real-world application you will need to add the code that retrieves the data from your database.

Creating Records

To create one or more hotels, you will add code like so:

case "createhotels":

    hotelJson = GetRequestPayload(context);

    try
    {
        hotels = JsonConvert.DeserializeObject(hotelJson, typeof(Hotel[])) as Hotel[];

        // Insert code to save hotels to the database here.

        // ** Make sure you assign the correct id to each hotel before sending result the client.
        result = "{\"success\":true,\"hotels\":" + JsonConvert.SerializeObject(hotels) + "}";
    }
    catch (Exception ex)
    {
        // Could not convert to Hotel[] instance;
    }
    if (hotels == null)
    {
        try
        {
            hotel = JsonConvert.DeserializeObject(hotelJson, typeof(Hotel)) as Hotel;

            // Insert code to save the hotel to the database here.

            // ** Make sure you assign the correct id to the hotel before sending result the client.
            result = "{\"success\":true,\"hotels\":[" + JsonConvert.SerializeObject(hotel) + "]}";
        }
        catch (Exception ex)
        {
            // Could not convert to Hotel instance;
        }
    }

    break;

The GetRequestPayload function allows you to extract the json-formatted data sent by the Sencha Touch application from the http request.

private string GetRequestPayload(HttpContext context)
{
    StreamReader r = new StreamReader(context.Request.InputStream);
    context.Request.InputStream.Position = 0;
    return r.ReadToEnd();
}

Updating Records

You will handle a request to update records with the following code:

case "updatehotels":

    hotelJson = GetRequestPayload(context);

    try
    {
        hotels = JsonConvert.DeserializeObject(hotelJson, typeof(Hotel[])) as Hotel[];

        // Insert code to update the hotels in the database here.

        result = "{\"success\":true,\"hotels\":" + JsonConvert.SerializeObject(hotels) + "}";
    }
    catch (Exception ex)
    {
        // Could not convert to Hotel[] instance;
    }

    if (hotels == null)
    {
        try
        {
            hotel = JsonConvert.DeserializeObject(hotelJson, typeof(Hotel)) as Hotel;

            // Insert code to update the hotel in the database here.

            result = "{\"success\":true,\"hotels\":[" + JsonConvert.SerializeObject(hotel) + "]}";
        }
        catch (Exception ex)
        {
            // Could not convert to Hotel instance;
        }
    }

    break;

Erasing Records

Finally, the following code allows you to handle the requests to erase records:

case "erasehotels":

    hotelJson = GetRequestPayload(context);

    try
    {
        hotels = JsonConvert.DeserializeObject(hotelJson, typeof(Hotel[])) as Hotel[];

        // Insert code to delete the hotels from the database here.

        result = "{\"success\":true,\"hotels\":" + JsonConvert.SerializeObject(hotels) + "}";
    }
    catch (Exception ex)
    {
        // Could not convert to Hotel[] instance;
    }

    if (hotels == null)
    {
        try
        {
            hotel = JsonConvert.DeserializeObject(hotelJson, typeof(Hotel)) as Hotel;
            // Here you would delete the hotel from the database...
            result = "{\"success\":true,\"hotels\":[" + JsonConvert.SerializeObject(hotel) + "]}";
        }
        catch (Exception ex)
        {
            // Could not convert to Hotel instance;
        }
    }

    break;

Testing the Connection Between the Store and the Endpoint

Having created the model, store, and the server endpoint, you can go ahead and create a simple Sencha Touch application that will allow you to test the store’s connection to its endpoint.

In the app.js file, create a Sencha Touch application like so:

Ext.application({
    name: 'App',
    stores: ['Hotels'],
    models: ['Hotel'],
    requires: ['Ext.util.DelayedTask'],
    launch: function () {

    }
});

Inside the launch method, you will obtain a reference to the Hotels store and define event handlers for its load and write events:

launch: function () {

    // Use the getStore function to get a reference to a store added via the stores config.
    var hotelsStore = Ext.getStore('Hotels');

    hotelsStore.on({
        load: this.onStoreLoad,
        write: this.onStoreWrite
    });

    hotelsStore.load();
}

You will use the onStoreLoad handler to perform create, update and delete operations on the store. This is how you will test your server endpoint:

onStoreLoad: function (store, records) {

    console.log('These are the hotels loaded:');
    store.data.each(function (item, index, length) {
        console.log('- ' + item.data.name);
    });

    store.getAt(0).set('name', 'Siesta by the sea');
    store.getAt(1).set('status', 1);

    // Calling sync should trigger a request to hotels.ashx?act=updatehotels. You can see the data send to the server in the request's headers.
    store.sync();
    // After calling sync, check out the request's header in the browser's console.
    // See the value of the Request Payload property.

    var updateTask = Ext.create('Ext.util.DelayedTask', function () {

        // Saving inside a delayed task, just to wait for the previous operations
        // to execute on the server and invoke the callbacks.
        var newHotel = Ext.create('App.model.Hotel', {
            name: 'Luna',
            address: 'The Moon\'s Surface',
            status: 0
        });

        // Sync will a request to hotels.ashx?act=createhotels
        store.add(newHotel)
        store.sync();

    }, this);

    updateTask.delay(3000);

    var removeTask = Ext.create('Ext.util.DelayedTask', function () {

        store.removeAt(0);
        store.sync();

    }, this);

    removeTask.delay(6000);

}

Let’s examine the code above in detail. The first operations are a couple of updates:

store.getAt(0).set('name', 'Siesta by the sea');
store.getAt(1).set('status', 1);
// Calling sync should trigger a request to hotels.ashx?act=updatehotels. You can see the data send to the server in the request's headers.
store.sync();

Then, you create and save a new hotel:

var updateTask = Ext.create('Ext.util.DelayedTask', function () {

    // Saving inside a delayed task, just to wait for the previous operations
    // to execute on the server and invoke the callbacks.
    var newHotel = Ext.create('App.model.Hotel', {
        name: 'Luna',
        address: 'The Moon\'s Surface',
        status: 0
    });

    // Sync will a request to hotels.ashx?act=createhotels
    store.add(newHotel)
    store.sync();

}, this);

updateTask.delay(3000);

Note that you are executing this code inside a delayed task to allow the previous update and its callback to be invoked.

And last, you delete a hotel model:

var removeTask = Ext.create('Ext.util.DelayedTask', function () {

    store.removeAt(0);
    store.sync();

}, this);

removeTask.delay(6000);

Processing the Server’s Response

The onStoreWrite handler will help you log the results of the requests made to the server:

onStoreWrite: function (store, operation, eOpts) {

    var action = operation.getAction();
    var responseText = operation.getResponse().responseText;
    var responsePayload = Ext.JSON.decode(responseText);
    var hotels = responsePayload.hotels;

    switch (action) {
        case 'create':
            console.log('CREATED RECORDS:');
            for (var i = 0; i < hotels.length; i++) {
                console.log(hotels[i].name);
            }
            break;
        case 'read':
            console.log('READ RECORDS:');
            for (var i = 0; i < hotels.length; i++) {
                console.log(hotels[i].name);
            }
            break;
        case 'update':
            console.log('UPDATED RECORDS:');
            for (var i = 0; i < hotels.length; i++) {
                console.log(hotels[i].name);
            }
            break;
        case 'destroy':
            console.log('DESTROYED RECORDS:');
            for (var i = 0; i < hotels.length; i++) {
                console.log(hotels[i].name);
            }
            break;
    }
}

The operation object encapsulates useful information about the responses sent by the endpoint. Here you are using the action config, which you can obtain by invoking the operation’s getAction method; and the server’s reponse, which you can obtain calling the operation’s getResponse method.

You can use the Ext.JSON.decode method to deserialize the reponse into an object that contains the hotels data sent by the endpoint.

Want To Learn More?

My Sencha Touch books will teach you how to create a Sencha Touch application, step by step, from mockups to production build.






Published at DZone with permission of Jorge Ramon, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)