Sencha Touch 2 – Navigation View, Templates and Forms

by
Tags: , , ,

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.


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 iconRecord 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 buttonRecord 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 textAdd 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.


FormNew 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.