Showing posts with label sencha touch. Show all posts
Showing posts with label sencha touch. Show all posts

Wednesday, February 15, 2012

Sencha Touch 2 - NavigationView, Templates and Forms

This post will build on the example application from the Introduction to Sencha Touch post. We will look at sorting, templates, navigation and forms.

Sorting

In the existing code new items appear at the bottom of the list. I’d prefer to see the newest items first. After a Sorter is added to the Store, Sencha displays records in the proper order.
var store = Ext.create('Ext.data.Store', {
    storeId: "activityStore",
    model: "Activity",
    proxy: {
        type: 'ajax',
        url: 'exercise.json'
    },
    sorters: [
        {
            property: 'date',
            direction: 'DESC'
        }
    ],
    autoLoad: true
});

Detail View

The user should able to drill into an Activity on the list to see the details.

Template

We will create an XTemplate that will be populated with the data from the Model. Templates can be created in Javascript or externalized. I prefer to put more complex templates in HTML rather than inline Javascript. Sencha recommends external templates are defined in a hidden textarea.
<textarea id="detail-template" style="display:none">
  <div>
    Date: {date:date("m/d/Y")} <br/>
    Type: {type} <br/>
    Distance: {distance} <br/>
    Time: {minutes} minutes <br/> 
    Notes: {comments} <br/>
  </div>    
</textarea>
The template is created once in code.
var template = Ext.XTemplate.from(Ext.get('detail-template'));

List item disclosure

Sencha displays a disclosure icon for each row when a function is assigned to the onItemDisclosure property of the list. Clicking on this icon allows the user to drill into the details for a row.
The onItemDisclosure function
  • receives the selected record as an argument
  • populates the template with the model’s data
  • creates a new panel
  • pushes the panel onto the navigation view
   {
       xtype: 'list',
       title: 'Activities',
       itemTpl: '{date:date("m/d/Y")} - {type}',
       store: store,
       onItemDisclosure: function (record, btn, index) {
           view.push({
               xtype: 'panel',
               title: 'Activity',
               html: template.apply(record.data),
               styleHtmlContent: true
           });
       }
   }

Pushing the panel onto the NavigationView handles a lot of stuff. The view becomes visible, the navigation bar title is set, a back button is displayed, navigation is handled automatically. This is a nice new feature in Sencha Touch 2.0.

Disclosure icon Record detail

Showing and hiding buttons

When the user is viewing the details, the Add button should disappear. We can manually hide the button in the onItemDisclosure before pushing the view.
onItemDisclosure: function (record, btn, index) {
                      view.down('#addButton').hide();
                      view.push({
                      // ...
The Add button needs to be shown again when the user navigates from the detail back to the main screen. Since the NavigationView handles the navigation automatically, we listen for the pop event and make the Add button is visible.
var onNavigationPop = function(v, item) {
   v.down('#addButton').show();
};

var view = Ext.create("Ext.NavigationView", {
   fullscreen: true,
   items: [
      // ...
   ],
   listeners: {
       pop: onNavigationPop
   }
});
Similarly, we can add onNavigationPush to hide the Add button whenever we push a new view on the stack. This is better than hiding the button in onItemDisclosure.
var onNavigationPush = function(v, item) {
    v.down('#addButton').hide();
};

var view = Ext.create("Ext.NavigationView", {
   fullscreen: true,
   items: [
      // ...
   ],
   listeners: {
       pop: onNavigationPop,
       push: onNavigationPush
   }
});

Record detail with button Record detail without button

Finally, instead of word Add on the button, I think it looks better with an icon.
view.getNavigationBar().add([
  {
      xtype: 'button',
      id: 'addButton',
      align: 'right',
      ui: 'action',
      iconCls: 'compose',
      iconMask: true, 
      handler: addNewRow
  }
]);

Add button text Add button icon

Adding new records with a form

The current implementation puts a fake row into the store when the Add button is pressed. We want to display a form to collect the user input.
We are going to change the implementation of addNewRow to create a FormPanel and push it onto the view. The NavigationView destroys components as they are popped, so we recreate the form every time. Alternately we could set autoDestroy to false on the NavigationView.
var addNewRow = function() {
    view.push(Ext.create('Ext.form.Panel', formBase));
};
The formBase object contains the form fields.
var formBase = {
    title: "Activity",
    itemId: "form",
    items: [
        {
            xtype: 'datepickerfield',
            name: 'date',
            label: 'Date',
            value: new Date()
        },
        {
            xtype: 'selectfield',
            name: 'type',
            label: 'Type',
            options: [
                { text: "" },
                { text: "Run", value: "Run" },
                { text: "Bike", value: "Bike" },
                { text: "Swim", value: "Swim" },
                { text: "Walk", value: "Walk" }
            ]
        },
        {
            xtype: 'textfield',
            name: 'distance',
            label: 'Distance'
        },
        {
            xtype: 'numberfield',
            name: 'minutes',
            label: 'Minutes'
        },
        {
            xtype: 'textfield',
            name: 'comments',
            label: 'Notes'
        }
    ]
};

Saving data

The navigation bar needs a save button. Initially this button will be hidden.
{
    xtype: 'button',
    id: 'saveButton',
    text: 'Save',
    align: 'right',
    ui: 'action',
    handler: save,
    hidden: true
}
The addNewRow function makes the save button visible.
var addNewRow = function() {
    view.push(Ext.create('Ext.form.Panel', formBase));
    view.down('#saveButton').show();
};
The save button is hidden in onNavigationPop.
var onNavigationPop = function(v, item) {
    v.down('#addButton').show();
    v.down('#saveButton').hide();
};
The save function
  • gets the form using the item id
  • creates an new Activity model from the form data
  • adds the new model to the store
  • navigates back to the list view
var save = function() {
   var form = view.down('#form');
   var record = Ext.create('Activity', form.getValues());
   store.add(record);
   // TODO validation and error handling
   view.pop();
};

Note that Sencha automatically displays the new data in the list without any effort on our part.

Form New row

Additional enhancements to improve this example could include things like validating form data, editing existing rows, sending data back to the server, converting to MVC.

The source code is available on github.

Monday, January 23, 2012

Introduction to Sencha Touch

Steve Smith recently looked at jQuery Mobile and Backbone using a simple application that allowed a user to track their daily exercise. I'm going to duplicate his example using Sencha Touch.

Sencha Touch is a framework for building mobile applications using HTML5, CSS3, and Javascript.  This example uses the Developer Preview of Sencha Touch 2.0.

Here's what our project looks like.

A Sencha Touch application is loaded from an HTML page. The HTML loads CSS and Javascript. Sencha Touch applications typically build the UI using Javascript so the index.html is very simple.

The touch/ directory contains the Sencha Javascript and CSS from the SDK.  Since this is an example, all if the application code is in one file, app.js. Larger applications will break the code up into more files and directories.

Sencha Application

Sencha Touch has a global namespace, Ext, that holds the framework and application code. The Ext.application helper function takes a configuration object where we define our application.

Ext.application({
    launch: function() {
        // define our app here
    }
});

Model

We extend Ext.data.Model to define a Model object describing our data. The data is supplied by the server in a JSON file, exercise.json. The fields in the model, match the properties in the JSON file.

Ext.define("Activity", {
    extend: "Ext.data.Model",
    fields: [
        {name: 'id', type: 'int'},
        {name: 'date', type: 'date'},    
        {name: 'type', type: 'string'},    
        {name: 'distance', type: 'string'},    
        {name: 'minutes', type: 'int'},  
        {name: 'comments', type: 'string'}
    ]
});
/* exercise.json */
[
    {
        "id":3,
        "date": "12/10/2011",
        "type": "Walk",
        "distance": "2.5 miles",
        "comments": "Shouldn't have taken the dog",
        "minutes": 45
    },
    ...
]  

Store

A Store is used to load and manage data. The Activity model we created is associated with the store. We define a proxy to load the JSON data from a URL. Finally, we tell the store to automatically load the data when the store is created.

var store = Ext.create('Ext.data.Store', {
    storeId: "activityStore",
    model: "Activity",
    proxy: {
        type: 'ajax',
        url: 'exercise.json'
    },
    autoLoad: true
});

Once the store is created, we can use the Javascript console to interact with the data.

User Interface

Now we need to display the data. A NavigationView shows a navigation bar and holds the List component. The list is defined in the code using the object literal syntax. The data store we created is assigned to the list so it can display the records. The itemTmp property defines how to display each item of data. This can be a template or a string.

var view = Ext.create("Ext.NavigationView", {
    fullscreen: true,
    items: [
        {
            xtype: 'list',
            title: 'Activities',
            itemTpl: '{date} - {type}',
            store: store
        }
    ]
});
Unformatted Date

This works but the date doesn't display very well. A format can be added to the template definition. {date} in the itemTpl becomes {date:date("m/d/Y")}.

var view = Ext.create("Ext.NavigationView", {
    fullscreen: true,
    items: [
        {
            xtype: 'list',
            title: 'Activities',
            itemTpl: '{date:date("m/d/Y")} - {type}',
            store: store
        }
    ]
});
Formatted Date

Adding New Data

Let's add a button to the header to add a new row of data.

view.getNavigationBar().add({
    xtype: 'button',
    text: 'Add',
    align: 'right',
    handler: addNewRow
});

The handler property of the button defines the function that is called whenever the button is pressed. Eventually we need a form so the user can add new data. For now, let's define a function addNewRow that will create a new row and add it to the store.

var addNewRow = function() {
    // future versions should display a form for adding a record
    var fakeRecord = Ext.create('Activity', {
        date: new Date(), 
        type: 'Walk', 
        distance: '2 miles', 
        minutes: 28, 
        comments: 'Auto generated record.'
    });

    store.add(fakeRecord);
};

Now when the button is pressed a new row appears in the UI.  The store notifies the list, so rows appear as records are added to the store.

Add Button

New Row

That should be enough to get you started. In future posts we'll look at features like sorting, detail views, navigation, and forms. The source code is available on github.

Friday, February 4, 2011

Philly ETE 2011 mobile line up!


 This post is via Kevin Griffin, Chariot Architect and committee chair of the Mobile tract for ETE:

 Philly ETE 2011 mobile line up!

Just thought I would post an update on the current lineup for Philly ETE.
I've been involved in helping get the mobile track lineup sorted out and so far we have confirmed the following:
  • Josh Clark - my favorite 'Tapworthy' author.
  • Johnathon Stark - He literally wrote the books on making mobile apps with HTML, CSS and JavaScript.
  • Ted Neward - If you are lazy, and want to write Android apps, he has the talk for you!
  • Jay McGavren - If you want to write Android apps, in Ruby (Oh yeah!) This is the talk for you (personally, I can't wait for this one!).
  • Terry Ryan - Philly Evangelist from Adobe who will use Air, to build Playbook apps. Magic!
  • David Kaneda - Although technically on the Frameworks track. Go build your mobile apps in Sencha Touch, seriously go now! That way you can thank him at ETE!
With one super secret invite still pending :D