Going Native with PhoneGap

With so many mobile devices in the market, more and more apps being developed. It kind of reminded me of the early day of the web when new browser came up one by one. In the mobile world, we have to deal with different devices and choose between web or native app. With HTML 5, our web app can do some of the things that a native app can. If you are familar with Sench Touch, the framework provides features such as touch event, geo location, offline storage and rich UI library. So, what are the things that may driver us to go native? at the top of the list is accessing the devices features & hardware (e.g. accelerometer, photo, contacts, vibration, etc). The 2nd driver could be $, meaning selling our apps in the App Store.

PhoneGap - Closing the Gap 🙂

Let me introduce PhoneGap to bridge the gap. As you can see from the diagram, PhoneGap is an open source framework that act as a bridge between web apps and mobile devices. Brilliant, har!! The framework allows you to:

  • package your web app code and resources into a binary app and deploy to multiple platforms (currently it supports 6 platforms; iOS, Android, Windows Phone 7 (coming soon), Blackberry, Palm/HP webOS and Symbian.
  • access native features such as accelerometer, camera, compass, contacts, file, geo location, storage, meaid and notification. The features x devices/OS matrix is available here.

Interestingly, You can also compile your code in the cloud via build.phonegap.com. It is still in beta, so it is free. The pricing details will be announced near its launch date.

My thought:

  • For now, building graphic intensive apps may not be suitable. Coding in the device native language may be better. But with the advancement in HTML 5, things may change.
  • The first stable release (v0.6.0) of PhoneGap was in Feb 2009 and now it is at version 0.9.4. Will there be big changes before it reaches 1.0?
  • Using PhoneGap means you must keep up with the devices OS version & PhoneGap. Hopefully the release can be fast enough since it is supporting so many devices.
  • For iOS, the framework integrate with XCode and it is very easy to jump start.
  • The documentation is good. It is organised base on the features and each has example.

Useful links:

I downloaded and started to play with the framework a little. I migrated my USGS app – which is based on Sencha Touch – to a native iPhone app. I have yet to explore its native APIs in depth. Just wanted to share the framework.

Happy hacking!!!

 

my USGS app in iPhone Simulator

Sencha Touch MVC Application – Part 2

In my previous post, I shared presentation on Sencha Touch MVC. That’s all the “talk”, it is time to see it in action. This short tutorial will walk through the implementation of Sencha Touch MVC application. I revamped my old (and long forgotten & messy) USGS apps as example. Before I dive into the code, I will share how to structure the apps.

Structuring Your Apps

Firstly, create the folder structure to support the MVC application. The figure below shows the basic structure required and the codes are available here. I personally think this is a good practice and can be use as coding standard and guideline for Sencha Touch apps. In addition to that, you may want to standardize the file & object naming convention for the controllers, views and data models.

  • m.usgs – the root folder for the application
    • app – folder for your sencha touch code, containing app.js and folders for controllers, models and views.
    • lib – folder for sencha library and other plug-in. (in my example, I moved the sencha library out for sharing with other apps)
    • resources – folder for web resources such as css & images.
    • index.html – the main page to launch the apps.
Sencha Touch - Structuring Your Apps for MVC
Sencha Touch - Structuring Your Apps for MVC

Coding

Step 1: Instantiating the Application

Create app/app.js and add the basic launcher for an Ext.Application instance. In the example it will be namespaced as “usgs”.

Ext.regApplication({
    name: "usgs",
    launch: function() {
	this.views.viewport = new this.views.Viewport();
    }
});

Create a main Viewport view which will house the pages required for displaying USGS Earthquake data. Here we place references to the card instances in the “usgs.views” namespace, so that we can refer to them explicitly in our page flow.

usgs.views.Viewport = Ext.extend(Ext.Panel, {
    fullscreen: true,
    layout: 'card',
    cardSwitchAnimation: 'slide',
    initComponent: function() {
        //put instances of cards into app.views namespace
        Ext.apply(usgs.views, {
        	usgsMenu: new usgs.views.UsgsMenu()
        	,usgsList: new usgs.views.UsgsList()
        	,usgsMap: new usgs.views.UsgsMap()
        });
        //put instances of cards into viewport
        Ext.apply(this, {
            items: [
                usgs.views.usgsMenu
                ,usgs.views.usgsList
                ,usgs.views.usgsMap
            ]
        });
        usgs.views.Viewport.superclass.initComponent.apply(this, arguments);
    },
    layoutOrientation : function(orientation, w, h) {
        usgs.views.Viewport.superclass.layoutOrientation.call(this, orientation, w, h);
    }
});

Step 2: Modelling Data

Like any MVC-based application the next step is to prepare for the data model. In the models folder, create a definition of the USGS model. The data structure match the content structure is USGS website. In order to demonstrate the use of JSON, I used YQL to convert the RSS data from USGS.

Ext.regModel("usgs.models.UsgsData", {
    fields: [
      {name: 'id', 		type: 'int'},
      {name: 'title', 		type: 'string'},
      {name: 'description', 	type: 'string'},
      {name: 'link', 		type: 'string'},
      {name: 'pubDate', 	type: 'date'},
      {name: 'lat', 		type: 'string'},
      {name: 'long', 		type: 'string'}
    ]
});

Create a simple data store to retrieve the data. For cross domain ajax, set the proxy type to ‘scripttag’. It also handles the callback. 🙂

usgs.stores.usgsData = new Ext.data.Store({
    model: 'usgs.models.UsgsData',
    proxy: {
    	type: 'scripttag',
    	url: 'http://query.yahooapis.com/v1/public/yql',
    	extraParams: {
    		format: 'json'
    	},
	  	reader: {
	  		root: 'query.results.item'
	  	}
  	}
});

Step 3: Creating the Views

The data is ready, now we start to create the view for the USGS data. It allows us to test the data store as well. In this example, I have 3 views:

  1. USGS Menu – this view contains list of data we can retrieve from USGS
  2. USGS List – based on the selected menu, this view shows the list of USGS earthquake data.
  3. USGS Map – this view shows the map where the earthquake occurred.

Here I will just show one of the view. Ignore the call to the controller (Ext.dispatch(…)) for now, wire the view when the controller is ready.

usgs.views.UsgsList = Ext.extend(Ext.Panel, {
    dockedItems: [{
        xtype: 'toolbar',
        title: 'USGS',
        dock: 'top',
        items: [{
        	xtype: 'button',
          text: 'Back',
          ui: 'back',
          handler: function() {
            Ext.dispatch({
                controller: usgs.controllers.usgsController,
                action: 'backToIndex'
            });
          },
          scope: this
        }]
    }],
    items: [{
        xtype: 'list',
        emptyText   : 'No data available.',
        store: usgs.stores.usgsData,
        itemTpl: '{title}',
        onItemDisclosure: function (record) {
            Ext.dispatch({
                controller: usgs.controllers.usgsController,
                action: 'showMap',
                data: record.data
            });
        },
        grouped: false,
        scroll: 'vertical',
        fullscreen: true
    }],
    initComponent: function() {
        usgs.views.UsgsList.superclass.initComponent.apply(this, arguments);
    }
});

Step 4: Building the Controller

We reached the last step and this is the point where all the action and view wired together. If you look back at the view, the handler dispatches specific action to the controller. The actions are defined in the methods below and the controller will then manage the page flow by activating the card. Switching page is done by calling the setActiveItem() and it is applicable because my Viewport is using card layout.

usgs.controllers.usgsController = new Ext.Controller({

    index: function(options) {
        usgs.views.viewport.setActiveItem(
            usgs.views.usgsMenu, options.animation
        );
    },

    showMap: function(options) {
        var data = options.data;
    	usgs.views.usgsMap.addMap(data);
        usgs.views.viewport.setActiveItem(
           usgs.views.usgsMap, options.animation
        );
    },

    showUsgsList: function(options) {
        var id = parseInt(options.id);
    	usgs.stores.usgsData.getProxy().extraParams.q = usgs.query[id].q;
    	usgs.stores.usgsData.read();
        usgs.views.viewport.setActiveItem(
            usgs.views.usgsList, options.animation
        );
    },

    backToIndex: function(options) {
        usgs.views.viewport.setActiveItem(
            usgs.views.usgsMenu, options.animation
        );
    },

    backToUsgsList: function(options) {
        usgs.views.viewport.setActiveItem(
            usgs.views.usgsList, options.animation
        );
    }
});

If you are not using card layout then the controller will need to render the UI component. In the sample code below the view is rendered using the Ext.Controller render() and listener is added to catch the event bubble from the view.

list: function() {
    this.listView = this.render({
        xtype: 'myMainPanel',
        listeners: {
            scope : this,
            filter: this.onFilter,
            selectionchange: this.onSelected
        }
    }, Ext.getBody()).down('.dataList');
}

The screenshots below are the result from the above tutorial.

Implementing the structure and MVC into Sencha Touch definitely helps to simplify my design. I can easily implement the page flow and not end up with spaghetti code. It also helps to separate the view, data and controller and make the code more maintainable.

Hope it is useful and happy hacking!

Sencha Touch MVC Application – Part 1

Developing MVC application is nothing new and there are many frameworks to help us implement MVC, e.g. Struts & Spring MVC (JEE), ASP.NET MVC, JavaScriptMVC, etc. If you are a mobile apps developer and using Sencha Touch, you will most likely wonder whether or not you can apply MVC to simplify your apps design and make it more maintainable.

Here I am sharing a useful presentation by Tommy Maintz (@tommymaintz). This presentation will bring you a step further into a better Sencha Touch mobile apps design by structuring your apps and applying MVC pattern.

Countdown to ExtJS 4

On the 16th Feb 2010 Sencha announced the availability of the first Ext JS 4 Developer Preview. Definitely an exciting news for the Ext JS developers, I can see from the comments in Sencha Blog that people cannot wait for the release. This is the biggest overhaul to the framework. For those who worry about migration, the team has taken that into consideration.

Ext JS 4 is completely sandboxed; there is no more native object augmentation or reliance on global variables. This means you can run Ext JS 4 alongside Ext JS 3 on the same page. To demonstrate this we’ve taken the best of both versions and combined them in a single page. We’ve used the Desktop example from Ext JS 3 and loaded Ext JS 4’s brand new charts alongside as if they were part of Desktop itself – check it out for yourself.

The second step is to provide as strong a legacy layer as possible to help you upgrade your Ext JS 3.x applications. We’re starting that today with the release of a detailed overview guide to the differences between the versions, and will update this as we continue our rollout. In addition to the guide we will be providing a legacy file that can be dropped in to an Ext JS 3.x application to help you upgrade your app. We’ll be expanding on both of these in another post very soon. – Sencha Blog

The following articles highlights the key features in Ext JS 4;

Ed Spencer from Sencha presented the introduction to Ext JS 4 and its architecture. You can see the slides below;

Last but not least, the examples and download is available;

Happy hacking!!

ExtJS and ASP.NET MVC 3: CRUD DataGrid

This short tutorial will walk though the implementation of DataGrid using ExtJS 3.3.1 and ASP.NET MVC 3. In this tutorial I focus more on the integration of the front-end, so you will not find any database code. Whenever we deal with data we usually create, read/retrieve, update or delete them. ExtJS provides a great data grid component and the ability to perform CRUD operation with very little coding. I used JSON as data format in the example below.

ExtJS DataGrid
ExtJS DataGrid

Let’s start with the server-side by developing the data model and controller.

Controller:

    public class ContactController : Controller
    {
        //
        // GET: /Contact/

        public ActionResult Index()
        {
            return View();
        }

        public JsonResult Load()
        {
            var contact = new List<Contact> {
                new Contact("Smith","95746325","smith@me.com"),
                new Contact("Adam","87291034","adam@me.com"),
                new Contact("Eve","98271345","eve@me.com"),
                new Contact("Chun Li","81728312","chun.li@me.com")
            };
            return Json(new
            {
                total = contact.Count,
                data = contact,
            }, JsonRequestBehavior.AllowGet);
        }

        [HttpPost]
        public JsonResult Create(List<Contact> data)
        {
            //insert Create code
            return Json(new
            {
                data = new Contact(data[0].Name, data[0].Phone, data[0].Email),
                success = true,
                message = "Create method called successfully"
            });
        }

        [HttpPost]
        public JsonResult Update(List<Contact> data)
        {
            //insert Update code
            return Json(new
            {
                success = true,
                message = "Update method called successfully"
            });
        }

        [HttpPost]
        public JsonResult Delete(List<string> data)
        {
            //insert Delete code
            return Json(new
            {
                success = true,
                message = "Delete method called successfully"
            });
        }
    }

Data Model:

    public class Contact
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Phone { get; set; }
        public string Email { get; set; }

        public Contact(string pName, string pPhone, string pEmail)
        {
            this.Id = System.Guid.NewGuid().ToString();
            this.Name = pName;
            this.Phone = pPhone;
            this.Email = pEmail;
        }

        public Contact() { }
    }

ExtJS:

Now, you move on to the view and work on the ExtJS. First, you define the record type which matches the server-side object.

        var Contact = Ext.data.Record.create([
            {
                name: 'Id',
                type: 'string'
            }, {
                name: 'Name',
                type: 'string'
            }, {
                name: 'Phone',
                type: 'string'
            }, {
                name: 'Email',
                type: 'string'
            }
        ]);

Now you need to setup the Writer and Reader

        var writer = new Ext.data.JsonWriter({
            encode: false,
            listful: true,
            writeAllFields: true
        });

        var reader = new Ext.data.JsonReader({
            totalProperty: 'total',
            successProperty: 'success',
            idProperty: 'Id',
            root: 'data',
            messageProperty: 'message'  // <-- New "messageProperty" meta-data
        }, Contact);

Then, setup a proxy to define the connection to the controller.

        var proxy = new Ext.data.HttpProxy({
            api: {
                read: '/Contact/Load',
                create: '/Contact/Create',
                update: '/Contact/Update',
                destroy: '/Contact/Delete'
            },
            headers: { 'Content-Type': 'application/json; charset=UTF-8' }
        });

Hooks the above components (reader, writer, proxy) to the store.

        var store = new Ext.data.Store({
            id: 'user',
            proxy: proxy,
            reader: reader,
            writer: writer,
            autoSave: false
        });

Add the data grid declaration

        var grid = new Ext.grid.GridPanel({
            store: store,
            columns: [
                { header: "Name",
                    width: 170,
                    sortable: true,
                    dataIndex: 'Name',
                    editor: {
                        xtype: 'textfield',
                        allowBlank: false
                    }
                },
                { header: "Phone No.",
                    width: 160,
                    sortable: true,
                    dataIndex: 'Phone',
                    editor: {
                        xtype: 'textfield',
                        allowBlank: false
                    }
                },
                { header: "EMail",
                    width: 170,
                    sortable: true,
                    dataIndex: 'Email',
                    editor: {
                        xtype: 'textfield',
                        allowBlank: false
                    }
                }
            ],
            plugins: [editor],
            title: 'Contacts DataGrid',
            height: 300,
            width: 510,
            tbar: [{
                iconCls: 'icon-user-add',
                text: 'Add Contact',
                handler: function () {
                    var e = new Contact({
                        Name: 'New Friend',
                        Phone: '(65) 89182736',
                        Email: 'new@google.com'
                    });
                    editor.stopEditing();
                    store.insert(0, e);
                    grid.getView().refresh();
                    grid.getSelectionModel().selectRow(0);
                    editor.startEditing(0);
                }
            }, {
                ref: '../removeBtn',
                iconCls: 'icon-user-delete',
                text: 'Remove Contact',
                handler: function () {
                    editor.stopEditing();
                    var s = grid.getSelectionModel().getSelections();
                    for (var i = 0, r; r = s[i]; i++) {
                        store.remove(r);
                    }
                }
            }, {
                iconCls: 'icon-user-save',
                text: 'Save All Modifications',
                handler: function () {
                    store.save();
                }
            }]
        });

Some observations:

  • When submitting data as JSON, set the headers “Content-Type” to “application/json” in the proxy object and set the encoding to false in the JsonWriter. If not, it will be treated as form submission.
  • Since I set the auto save to false. DataGrid will submit list of contact when there are 2 or more changes, however it will send a single object when there is 1 change. In order to make it consistent to send as list, set list full to true in the JsonWriter.
  • ASP.NET MVC 3 able to recognize the JSON without additional coding.

If you want to see the complete source code, download it from here. Hope it’s useful. 🙂

Happy hacking!