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 50 posts at DZone. You can read more from them at their website. View Full User Profile

How to Create a Sencha Touch 2 App: Part 4

04.30.2012
| 4829 views |
  • submit to reddit

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.

Deleting Records from a Sencha Touch Data Store

The Delete Note workflow begins when a user taps the Delete Button on the Note Editor View:

This Button needs a tap handler, which we will add to the NoteEditor Class, in the NoteEditor.js file:

var deleteButton = {
    xtype: "button",
    iconCls: "trash",
    iconMask: true,
    handler: this.onDeleteButtonTap,
    scope: this
};

As we did with the Save Button, we are using the handler and scope configs to map the function that will handle tap events on the Button, as well as to pass the View as the scope for the handler function.

Of course, we need to add the onDeleteButtonTap() function to the NoteEditor Class:

onDeleteButtonTap: function () {
    console.log("deleteNoteCommand");
    this.fireEvent("deleteNoteCommand", this);
}

This is the same pattern we’ve used to emit events from the Views throughout the application. We capture the event triggered from a control in the View, and create a View event that is in turn captured by the Controller.

Over in the Notes Controller, we are going to map a handler function to the deleteNoteCommand event fired by the Note Editor View. We will do this in the control config, under the noteEditor key:

control: {
    notesListContainer: {
        // The commands fired by the notes list container.
        newNoteCommand: "onNewNoteCommand",
        editNoteCommand: "onEditNoteCommand"
    },
    noteEditor: {
        // The commands fired by the note editor.
        saveNoteCommand: "onSaveNoteCommand",
        deleteNoteCommand: "onDeleteNoteCommand"
    }
}

Now we can implement the onDeleteNoteCommand() function like so:

onDeleteNoteCommand: function () {

    console.log("onDeleteNoteCommand");

    var noteEditor = this.getNoteEditor();
    var currentNote = noteEditor.getRecord();
    var notesStore = Ext.getStore("Notes");

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

    this.activateNotesList();
}

Here, we acquire references to the Note Editor, the note loaded into the editor, and the Notes Store. Remember that the getNoteEditor() function is a routine the framework created for us when we declared the editor in the refs config:

refs: {
    // We're going to lookup our views by xtype.
    notesListContainer: "noteslistcontainer",
    noteEditor: "noteeditor"
}

Our next steps in onDeleteNoteCommand() are to remove the current note from the store and make the changes permanent:

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

Finally, we activate the Notes List Container View:

this.activateNotesList();

Another quick check on the emulator should confirm that at this point we are able to delete notes.

Navigating Back To the Main View

In order to navigate from the Note Editor View back to the Notes List Container View without making any changes to a note, we need to add a tap handler for the Home Button in the Note Editor Class:

var backButton = {
    xtype: "button",
    ui: "back",
    text: "Home",
    handler: this.onBackButtonTap,
    scope: this
};

We will define the onBackButtonTap() function as follows:

onBackButtonTap: function () {
    console.log("backToHomeCommand");
    this.fireEvent("backToHomeCommand", this);
}

In the Controller, we will map this event to the onBackToHomeCommand() handler function:

control: {
    notesListContainer: {
        // The commands fired by the notes list container.
        newNoteCommand: "onNewNoteCommand",
        editNoteCommand: "onEditNoteCommand"
    },
    noteEditor: {
        // The commands fired by the note editor.
        saveNoteCommand: "onSaveNoteCommand",
        deleteNoteCommand: "onDeleteNoteCommand",
        backToHomeCommand: "onBackToHomeCommand"
    }
}

And the onBackToHomeCommand() function will look like this:

onBackToHomeCommand: function () {

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

At this point, we can use the emulator to check that a tap on the Home Button activates the Notes List Container View.

Setting Up Grouping in a Sencha Touch List

One important usability detail we are missing is the ability to render the cached notes grouped by date. It’s amazing how easily we can accomplish this in Sencha Touch. Let’s first define a grouper config for the Notes Store:

Ext.define("NotesApp.store.Notes", {
    extend: "Ext.data.Store",
    requires:"Ext.data.proxy.LocalStorage",
    config: {
        model: "NotesApp.model.Note",
        proxy: {
            type: 'localstorage',
            id: 'notes-app-store'
        },
        sorters: [{ property: 'dateCreated', direction: 'DESC'}],
        grouper: {
            sortProperty: "dateCreated",
            direction: "DESC",
            groupFn: function (record) {

                if (record && record.data.dateCreated) {
                    return record.data.dateCreated.toDateString();
                } else {
                    return '';
                }
            }
        }
    }
});

As of this writing, groupers are not explained very well in Sencha Touch’s documentation. However, it is not difficult to make sense of this config’s properties. The groupFn config is the function used to generate the label for the group. In our case, the label will be the date the notes were taken:

The sortProperty config defines the value that will be used to sort the groups. If you do not include this config, the fields will be sorted based on the value returned by the function defined with the groupFn config. The direction config specifies the direction to sort the groups.

The last change needed to implement grouping consists of adding the grouped config to the NotesList Class:

Ext.define("NotesApp.view.NotesList", {
    extend: "Ext.dataview.List",
    alias: "widget.noteslist",
    config:{
        scrollable:'vertical'
    },
    config: {
        loadingText: "Loading Notes...",
        emptyText: "</pre>
<div class="\"notes-list-empty-text\"">No notes found.</div>
<pre>
",
        onItemDisclosure: true,
        grouped: true,
        itemTpl: "</pre>
<div class="\"list-item-title\"">{title}</div>
<div class="\"list-item-narrative\"">{narrative}</div>
<pre>
"
    }
});

When we set the grouped config to true, the List will use the groups defined in its store, through the grouper config, to render its items appropriately.

Let’s check how the list looks after we turned on grouping. Start the emulator and confirm that the notes list has date groups similar to the following screenshot:

Summary

We just finished building all the features of the Note Editor View by adding both, the ability to delete notes, and the ability to navigate back to the Notes List Container View when the Home Button is tapped.

We also modified the Notes List so the notes are rendered in groups based on the date the notes were saved. This makes it easier for our users to find their notes.

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-Part4.zip

 

 

 

 

 

 

 

 

 

 

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