Sencha Touch 2 Models – Loading And Saving Model Data Using a Proxy, PHP Example

Sencha Touch models have the ability to work with a proxy. This feature allows you to save and retrieve model data from the server, memory or local storage, without depending on a Sencha Touch data store.

The model methods you will learn in this article are the following:

  • save
  • erase

Let’s try them with a very simple scenario where the server side is a PHP page. In this example you will create a simple Sencha Touch application with the following files:

In the model/Hotel.js file, you will define the 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' }
        ],
        proxy: {
            type: 'ajax',
            api: {
                create: '../../services/hotels.php?act=createhotel',
                read: '../../services/hotels.php?act=loadhotel',
                update: '../../services/hotels.php?act=updatehotel',
                destroy: '../../services/hotels.php?act=erasehotel'
            },
            reader: {
                rootProperty:'hotels'
            }
        }
    }
});

[Read more...]

Sencha Touch 2 Models – Loading And Saving Model Data Using a Proxy, C# Example

Sencha Touch models have the ability to work with a proxy. This feature allows you to save and retrieve model data from the server, memory or local storage, without depending on a Sencha Touch data store.

The model methods you will learn in this article are the following:

  • save
  • erase

Let’s try them with a very simple scenario where the server side is a .NET handler written in C#. In this example you will create a simple Sencha Touch application with the following files:

In the model/Hotel.js file, you will define the 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' }
        ],
        proxy: {
            type: 'ajax',
            api: {
                create: '../../services/hotels.ashx?act=createhotel',
                read: '../../services/hotels.ashx?act=loadhotel',
                update: '../../services/hotels.ashx?act=updatehotel',
                destroy: '../../services/hotels.ashx?act=erasehotel'
            },
            reader: {
                rootProperty:'hotels'
            }
        }
    }
});

[Read more...]

Sencha Touch 2 Models – Custom Validation Functions

Occasionally, the built-in validations provided by the Ext.data.Validations singleton are not enough to satisfy your application’s requirements. Fortunately, it is possible to augment the Ext.data.Validations class with validation methods of our own.

For example, in the Room.js file we created in the previous Sencha Touch Model validations article of this series, you could add a roomRate validation like so:

Ext.define('App.model.Room', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            { name: 'id', type: 'int' },
            { name: 'floor', type: 'string' },
            { name: 'type', type: 'string' },
            { name: 'standardRate', type: 'float' },
            { name: 'smoking', type: 'string' },
            { name: 'status', type: 'int' }
        ],
        validations: [
            { type: 'presence', field: 'floor' },
            { type: 'inclusion', field: 'type', list: ['single', 'double', 'suite'], message: 'valid values are "single", "double" and "suite"' },
	    { type: 'roomRate', field: 'standardRate', message: 'must be between $100 and $1000 per night' }
        ]
    }
});

The roomRate is a type of validation that allows you to restrict a room’s rate to the $100 to $1000 per night range. In the index.html file, you will type the following code:

Ext.application({
    name: 'App',
    requires: ["Ext.data.Validations"],
    models: ["Room"],
    launch: function () {

        Ext.applyIf(Ext.data.Validations, {
            roomRate: function (config, value) {
                // No configs read, so allow just value to be passed
                if (arguments.length === 1) {
                    value = config;
                }

                return value > 99 && value < 1001;
            }
        });

        var myRoom = Ext.create('App.model.Room', {
            id: 1,
            standardRate: 5,
            type: 'Royal Suite',
            status: 1
        });

        // Validating a model.
        var errors = myRoom.validate();

        // Errors is a collection
        console.log('Number of errors: ' + errors.length);

        // Each error has a field and a message property.
        errors.each(function (item, index, length) {
            console.log('Field "' + item.getField() + '" ' + item.getMessage());
        });
    }
});

If you run the application in Google Chrome and enable Chrome’s JavaScript console, you should see an output similar to this:

Sencha Touch Models Custom Validation

In Detail

It all starts with the model’s validations array, where you create an entry for the roomRate validation:

validations: [
	{ type: 'presence', field: 'floor' },
	{ type: 'roomRate', field: 'standardRate', message: 'must be between $100 and $1000 per night' },
	{ type: 'inclusion', field: 'type', list: ['single', 'double', 'suite'], message: 'valid values are "single", "double" and "suite"' }
]

This entry specifies the roomRate type, which does not exist in the Ext.data.Validations class, along with a value for the message property.

Then, you implement the new validation in the launch function of the application, using Ext.applyIf to add the roomRate method to the Ext.data.Validations class:

Ext.applyIf(Ext.data.Validations, {
	roomRate: function (config, value) {
		// If config was not passed, make value the config.
		if (arguments.length === 1) {
			value = config;
		}

		return value > 99 && value < 1001;
	}
});

As you can imagine, you can use this approach to create any type of validation logic needed by your application.

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.

My New Sencha Touch 2 Book

I just published Building a Sencha Touch Application. The book will teach you how to create a Sencha Touch 2 application step by step, from mockups to production build.

Here’s is a summary of the subjects you will learn:

  • The building blocks of a Sencha Touch application.
  • How to implement the Model-View-Controller pattern in a Sencha Touch application.
  • How to create an application with multiple views, and how to implement an intuitive navigation pattern between them.
  • How to create list-based and master-detail user interfaces in a Sencha Touch application.
  • How to capture, edit and validate data using Sencha Touch form-based user interfaces.
  • How Sencha Touch stores data on the device using HTML5 local storage.
  • How to use the Sencha SDK Tools to create a production build of your application.

Check It Out

You can preview the book here: Book Preview - Building a Sencha Touch Application.

You can review the table of contents and download the book here: Building a Sencha Touch Application.

Thanks to the Reviewers

I would like to thank @diederikveelo, @royhack and @3advance. They helped me make a better book for you.

What Do You Think?

I would also like to hear what you think about the book and how I can make the next version better. Please leave a comment if you have a few minutes.

Sencha Touch 2 Models – Model Validations

In this article we are going to explore Sencha Touch model validations.

Validations are a set of functions used to validate the values of a model’s fields. Their functionality is encapsulated in the Validations class, which is a singleton.

You set up model validations through the model’s validations config. Sencha Touch defines built-in validations of the following types:

  • email, which validates that a field’s value is in the format of an email address:
    validations: [{ type: 'email', field: 'emailAddress'}]
    
  • exclusion, which validates that a field’s value is not present in a predefined list of values:
    validations: [{ type: 'exclusion', field: 'username', list: ['Administrator', 'PowerUser', 'Guest', 'Everyone']}]
    
  • inclusion, which validates that a field’s value is present in a predefined list of values:
    validations: [{ type: 'inclusion', field: 'size', list: ['Large', 'Medium', 'Small']}]
    
  • format, which validates that a field’s value conforms to a format defined by a configured regular expression:
    validations: [{ type: 'format', field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}]
    
  • length, which validates that the length a field’s value is between the configured minimum and maximum values:
    validations: [{ type: 'length', field: 'password', min: 7, max: 16}]
    
  • presence, which validates that a given field is assigned a value:
    validations: [{ type: 'presence', field: 'firstName'}]
    

After setting up validations, you use them by invoking the validate function on the model. Let’s see an example.

Validating a Model

First, let’s set up a sample application with a set of directories like this:

Our index.html file must contain references to the framework’s files in its head section:

<link href="../../../lib/st2/resources/css/sencha-touch.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="../../../lib/st2/sencha-touch-debug.js"></script>
<script type="text/javascript">
        // Application goes here.
</script>

In the Room.js file, let’s define the Room model as follows:

Ext.define('App.model.Room', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            { name: 'id', type: 'int' },
            { name: 'floor', type: 'string' },
            { name: 'type', type: 'string' },
            { name: 'standardRate', type: 'float' },
            { name: 'smoking', type: 'string' },
            { name: 'status', type: 'int' }
        ],
        validations: [
            { type: 'presence', field: 'floor' },
            { type: 'length', field: 'standardRate', min: 2 },
            { type: 'inclusion', field: 'type', list: ['single', 'double', 'suite'] }
        ]
    }
});

Note the validations config, where we defined validations of types presence, length and inclusion.

Let’s switch over to the index.html file and validate a model instance:

Ext.application({
    name: 'App',
    models: ['Room'],
    launch: function () {

        var myRoom = Ext.create('App.model.Room', {
            id: 1,
            standardRate: 5,
            type: 'Royal Suite',
            status: 1
        });

        // Validating a model.
        var errors = myRoom.validate();

        // Errors is a collection
        console.log('Number of errors: ' + errors.getCount());

        errors.each(function (item, index, length) {
            // Each item in the errors collection is an instance of the Ext.data.Error class.
            console.log('Field "' + item.getField() + '" ' + item.getMessage());
        });
    }
});

If we open the index.html in a WebKit browser and examine the JavaScript console, we will see an output like this:

In Detail

The first detail we need to look at is how we define model validations, something we did in the Room.js file:

Ext.define('App.model.Room', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            { name: 'id', type: 'int' },
            { name: 'floor', type: 'string' },
            { name: 'type', type: 'string' },
            { name: 'standardRate', type: 'float' },
            { name: 'smoking', type: 'string' },
            { name: 'status', type: 'int' }
        ],
        validations: [
            { type: 'presence', field: 'floor' },
            { type: 'length', field: 'standardRate', min: 2 },
            { type: 'inclusion', field: 'type', list: ['single', 'double', 'suite'] }
        ]
    }
});

These validations mean that the floor field is required, the value of the standardRate field must have at least two characters, and the value of the type field must be ‘single’, ‘double’ or ‘suite’.

In the application, we first create an instance of the model:

var myRoom = Ext.create('App.model.Room', {
    id: 1,
    standardRate: 5,
    type: 'Royal Suite',
    status: 1
});

Then, we invoke the validation function:

// Validating a model.
var errors = myRoom.validate();

The validate method returns an instance of the Ext.data.Errors class. This utility class contains a collection of objects representing each of the validation errors that occurred. It also contains helper functions that allow you to retrieve information about each error, such as the field that caused the error condition and the error message.

This is how you can use the Errors object:

// Errors is a collection
console.log('Number of errors: ' + errors.getCount());

errors.each(function (item, index, length) {
    // Each item in the errors collection is an instance of the Ext.data.Error class.
    console.log('Field "' + item.getField() + '" ' + item.getMessage());
});

Note how each error has a getField and getMessage function. These are the methods you can use to determine what field caused the error condition and what is the error message.

You can also use the getByField function of the errors object, which itself returns an array with the errors for the given field:

errors.getByField('floor');  // returns [{field: 'floor',  message: 'Field "floor" must be present'}]

What’s Next

In the next article of this series we will continue our tour of Sencha Touch 2 models. Stay tuned!

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.

Sencha Touch 2 Models – Creating and Editing Model Instances

Introduction to Models

In this Sencha Touch 2 tutorial we are going to learn how to create a model, and how to edit model instances.

In general, a Sencha Touch application will consist of one or more views, one or more models and stores, and one or more controllers. Views have a dual role: they render representations of the data in the models, and capture and transmit user input to the controllers. Controllers will translate the user’s input into changes in the data and/or the behavior of the application. Models represent the application’s data and state.

Creating and Editing Model Instances

A Sencha Touch 2 model inherits from the Ext.data.Model class. Let’s create a hypothetical model to represent a hotel. First, let’s set up a sample application with a set of directories like this:

Then, our index.html file, which should include references to the framework’s files in its head section:

<link href="../../../lib/st2/resources/css/sencha-touch.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="../../../lib/st2/sencha-touch-debug.js"></script>
<script type="text/javascript">
        // Application goes here.

</script>

Now, in the Hotel.js file, let’s define the Hotel model as follows:


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' }
        ]
    }
});

In the index.html file, we are going to define our application with the following code:

Ext.application({
    name: 'App',
    models: ['Hotel'],
    launch: function () {

        var myHotel = Ext.create('App.model.Hotel', {
            id: 1,
            name: 'Siesta by the Ocean',
            address: '1 Ocean Front, Happy Island',
            status: 1
        });

        // Getting a field's value.
        console.log('Hotel name is ' + myHotel.get('name'));

        // Setting a field's value.
        myHotel.set('status', 0);

        var statusMsg = 'Active';

        if (myHotel.get('status') === 0) {
            statusMsg = 'Inactive';
        }

        console.log('Hotel is ' + statusMsg);
    }
});

If we open the index.html in a WebKit browser and examine the JavaScript console, we will see an output like this:

In Detail

The first detail we need to look at is how we declare a model. This happened in the Hotel.js file. In one of its most basic forms, a model consists of a set of fields, defined inside the class’ config object:

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' }
        ]
    }
});

The next detail is how we make the application aware of a model. This happens in the app’s models config:

models: ['Hotel']

This will cause the application to load the model’s file, Hotel.js, for us. We did not have to include this file in the index.html file.

After we do this, we create an instance of the model inside the launch function:

var myHotel = Ext.create('App.model.Hotel', {
    id: 1,
    name: 'Siesta by the Ocean',
    address: '1 Ocean Front, Happy Island',
    status: 1
});

Acquiring the value of a field is accomplished via the model’s get function:

// Getting a field's value.
console.log('Hotel name is ' + myHotel.get('name'));

Setting a field’s value is equally easy using with set function:

// Setting a field's value.
myHotel.set('status', 0);

What’s Next

In the next article of this series we will continue our tour of Sencha Touch 2 models. Stay tuned!

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.

How to Create a Sencha Touch 2 App, Part 5

Many of the readers of this tutorial on how to build a Sencha Touch Application have requested a version of the Notes Application that shows how to create components using the config object, instead of the initialize function. In this chapter of the tutorial, we are going to do just that.

Using Sencha Touch’s Config Object to Create the Notes List

The first step we are going to take is consolidate the Notes List Container View and Notes List View into a single View, which we will call Notes List View. This View has the same components that used to exist in the former Views:

In the application’s view directory, we are going to delete the old NotesListContainer.js file, and leave only the NotesList.js and NoteEditor.js files:

Next, we are going to remove the existing code from the NotesList.js file, and define the NotesList class like so:

Ext.define("NotesApp.view.NotesList", {
    extend: "Ext.Container",
    requires:"Ext.dataview.List",
    alias: "widget.noteslistview",

    config: {
        layout: {
            type: 'fit'
        },
        items: [{
            xtype: "toolbar",
            title: "My Notes",
            docked: "top",
            items: [
                { xtype: 'spacer' },
                {
                    xtype: "button",
                    text: 'New',
                    ui: 'action',
                    itemId: "newButton"
                }
            ]
        }, {
            xtype: "list",
            store: "Notes",
            itemId:"notesList",
            loadingText: "Loading Notes...",
            emptyText: '<div class="notes-list-empty-text">No notes found.</div>',
            onItemDisclosure: true,
            grouped: true,
            itemTpl: '<div class="list-item-title">{title}</div><div class="list-item-narrative">{narrative}</div>'
        }],
        listeners: [{
            delegate: "#newButton",
            event: "tap",
            fn: "onNewButtonTap"
        }, {
            delegate: "#notesList",
            event: "disclose",
            fn: "onNotesListDisclose"
        }]
    },
    onNewButtonTap: function () {
        console.log("newNoteCommand");
        this.fireEvent("newNoteCommand", this);
    },
    onNotesListDisclose: function (list, record, target, index, evt, options) {
        console.log("editNoteCommand");
        this.fireEvent('editNoteCommand', this, record);
    }
});

If you are already familiar with the previous version of this View, you will notice that although the onNewButtonTap and onNotesListDisclose functions remain unchanged, we are now taking advantage og the config object to define the View’s items, and the event listeners needed for the New button and the disclose buttons of the notes List.

Defining Event Listeners with Sencha Touch Config Objects

The New button’s tap listener is pretty straightforward. Its delegate config is the value of the itemId config of the button. The fn config is a pointer to the onNewButtonTap function:

{
    delegate: "#newButton",
    event: "tap",
    fn: "onNewButtonTap"
}

We defined the List’s disclose handler in a similar fashion. The delegate points to the List’s itemId, and fn is the onNotesListDisclose function:

{
    delegate: "#notesList",
    event: "disclose",
    fn: "onNotesListDisclose"
}

Configuring the Note Editor

Now we are going to move on to the NoteEditor View, where we will replace the initialize function with the config’s items and listeners properties:

Ext.define("NotesApp.view.NoteEditor", {
    extend: "Ext.form.Panel",
    requires: "Ext.form.FieldSet",
    alias: "widget.noteeditorview",
    config: {
        scrollable: 'vertical',
        items: [
            {
                xtype: "toolbar",
                docked: "top",
                title: "Edit Note",
                items: [
                    {
                        xtype: "button",
                        ui: "back",
                        text: "Home",
                        itemId: "backButton"
                    },
                    { xtype: "spacer" },
                    {
                        xtype: "button",
                        ui: "action",
                        text: "Save",
                        itemId: "saveButton"
                    }
                ]
            },
            {
                xtype: "toolbar",
                docked: "bottom",
                items: [
                    {
                        xtype: "button",
                        iconCls: "trash",
                        iconMask: true,
                        itemId: "deleteButton"
                    }
                ]
            },
            { xtype: "fieldset",
                items: [
                    {
                        xtype: 'textfield',
                        name: 'title',
                        label: 'Title',
                        required: true
                    },
                    {
                        xtype: 'textareafield',
                        name: 'narrative',
                        label: 'Narrative'
                    }
                ]
            }
        ],
        listeners: [
            {
                delegate: "#backButton",
                event: "tap",
                fn: "onBackButtonTap"
            },
            {
                delegate: "#saveButton",
                event: "tap",
                fn: "onSaveButtonTap"
            },
            {
                delegate: "#deleteButton",
                event: "tap",
                fn: "onDeleteButtonTap"
            }
        ]
    },
    onSaveButtonTap: function () {
        console.log("saveNoteCommand");
        this.fireEvent("saveNoteCommand", this);
    },
    onDeleteButtonTap: function () {
        console.log("deleteNoteCommand");
        this.fireEvent("deleteNoteCommand", this);
    },
    onBackButtonTap: function () {
        console.log("backToHomeCommand");
        this.fireEvent("backToHomeCommand", this);
    }

});

We are following the same approach we used to configure the NotesList View. This time, we need listeners for the Back, Save and Delete buttons:

listeners: [
    {
        delegate: "#backButton",
        event: "tap",
        fn: "onBackButtonTap"
    },
    {
        delegate: "#saveButton",
        event: "tap",
        fn: "onSaveButtonTap"
    },
    {
        delegate: "#deleteButton",
        event: "tap",
        fn: "onDeleteButtonTap"
    }
]

The onBackButtonTap, onSaveButtonTap, and onDeleteButtonTap function remain unchanged.

Adding View Instances to the Application

In the app.js file, we are going to instantiate both Views as follows:

Ext.application({
    name: "NotesApp",

    models: ["Note"],
    stores: ["Notes"],
    controllers: ["Notes"],
    views: ["NotesList", "NoteEditor"],

    launch: function () {

        var notesListView = {
            xtype: "noteslistview"
        };
        var noteEditorView = {
            xtype: "noteeditorview"
        };

        Ext.Viewport.add([notesListView, noteEditorView]);

    }
});

Modifying the Controller

The Controller remains unchanged, with the exception of the Views aliases, which we have changed in the chapter of the tutorial:

Ext.define("NotesApp.controller.Notes", {

    extend: "Ext.app.Controller",
    config: {
        refs: {
            // We're going to lookup our views by alias.
            notesListView: "noteslistview",
            noteEditorView: "noteeditorview",
            notesList: "#notesList"
        },
        control: {
            notesListView: {
                // The commands fired by the notes list container.
                newNoteCommand: "onNewNoteCommand",
                editNoteCommand: "onEditNoteCommand"
            },
            noteEditorView: {
                // The commands fired by the note editor.
                saveNoteCommand: "onSaveNoteCommand",
                deleteNoteCommand: "onDeleteNoteCommand",
                backToHomeCommand: "onBackToHomeCommand"
            }

        }
    },
    // Transitions
    slideLeftTransition: { type: 'slide', direction: 'left' },
    slideRightTransition: { type: 'slide', direction: 'right' },

    // Helper functions
    getRandomInt: function (min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    },
    activateNoteEditor: function (record) {

        var noteEditorView = this.getNoteEditorView();
        noteEditorView.setRecord(record); // load() is deprecated.
        Ext.Viewport.animateActiveItem(noteEditorView, this.slideLeftTransition);
    },
    activateNotesList: function () {
        Ext.Viewport.animateActiveItem(this.getNotesListView(), this.slideRightTransition);
    },

    // Commands.
    onNewNoteCommand: function () {

        console.log("onNewNoteCommand");

        var now = new Date();
        var noteId = (now.getTime()).toString() + (this.getRandomInt(0, 100)).toString();

        var newNote = Ext.create("NotesApp.model.Note", {
            id: noteId,
            dateCreated: now,
            title: "",
            narrative: ""
        });

        this.activateNoteEditor(newNote);

    },
    onEditNoteCommand: function (list, record) {

        console.log("onEditNoteCommand");

        this.activateNoteEditor(record);
    },
    onSaveNoteCommand: function () {

        console.log("onSaveNoteCommand");

        var noteEditorView = this.getNoteEditorView();

        var currentNote = noteEditorView.getRecord();
        var newValues = noteEditorView.getValues();

        // Update the current note's fields with form values.
        currentNote.set("title", newValues.title);
        currentNote.set("narrative", newValues.narrative);

        var errors = currentNote.validate();

        if (!errors.isValid()) {
            Ext.Msg.alert('Wait!', errors.getByField("title")[0].getMessage(), Ext.emptyFn);
            currentNote.reject();
            return;
        }

        var notesStore = Ext.getStore("Notes");

        if (null == notesStore.findRecord('id', currentNote.data.id)) {
            notesStore.add(currentNote);
        }

        notesStore.sync();

        notesStore.sort([{ property: 'dateCreated', direction: 'DESC'}]);

        this.activateNotesList();
    },
    onDeleteNoteCommand: function () {

        console.log("onDeleteNoteCommand");

        var noteEditorView = this.getNoteEditorView();
        var currentNote = noteEditorView.getRecord();
        var notesStore = Ext.getStore("Notes");

        notesStore.remove(currentNote);
        notesStore.sync();

        this.activateNotesList();
    },
    onBackToHomeCommand: function () {

        console.log("onBackToHomeCommand");
        this.activateNotesList();
    },

    // Base Class functions.
    launch: function () {
        this.callParent(arguments);
        var notesStore = Ext.getStore("Notes");
        notesStore.load();
        console.log("launch");
    },
    init: function () {
        this.callParent(arguments);
        console.log("init");
    }
});

Summary

We just created a version of the Notes Application where, instead of the initialize function, we exclusively used config objects to configure each of the application’s Views. In the process, we learned how to create event listeners for components defined using config objects.

At this point, we have accomplished our goals for this application. I hope the insights you gained in this Sencha Touch tutorial will help you create great mobile applications.

Downloads

Download the source code for this article: NotesApp-ST2-Part5.zip

The Entire Series

Want To Learn More?

My Sencha Touch books will teach you how to create an application from scratch.

What Sencha Touch Topics Would You Like Me To Write About?

I’d like to help you learn Sencha Touch better and faster, but I need you to let me know what subjects you are curious about, or what topics you are struggling with.

Please send me your questions or suggestions for future posts at miamicoder[AT]gmail.com.

How to Create a Sencha Touch 2 App, Part 4

This is the fourth part of my tutorial on how to create a Sencha Touch 2 application. In this article, we are going to complete the following tasks:

  • Add the delete note feature to the Note Editor View.
  • Implement the navigation back to the Notes List Container View when the Home button in the Note Editor View is tapped.
  • Modify the Notes List Container View so it renders the notes grouped by date.

[Read more...]