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

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

How to Create a Sencha Touch 2 App, Part 3

In this third part of this tutorial on how to create a Sencha Touch application, we are going to create the Note Editor View. This is the View that will allow our users to create, edit and delete notes.

When we finish this article, our application will have the ability to create notes, and edit existing notes. Let’s get started building the Note Editor View.

[Read more...]

How to Create a Sencha Touch 2 App, Part 2

In this second part of the tutorial on how to build a Sencha Touch 2 application we will continue building a small application that allows people to save notes on the device running the app.

So far, we have been working on the View that renders the list of notes cached on the device:

[Read more...]

How to Define Application Icons in Sencha Touch

The Custom Icon and Image Creation Guidelines for iOS applications recommend different web clip and application icon sizes for phones and tablets. Sencha Touch provides a means to define these icons in the Application Class. The icons will be shown on the home screen for iPhone and iPad applications.

These are the web clip and Application icon sizes, as recommended by the guidelines:

  • Size for iPhone and iPod touch (in pixels): 57 x 57
  • Size for high-resolution iPhone and iPod touch (in pixels): 114 x 114
  • Size for iPad (in pixels): 72 x 72
  • Size for high-resolution iPad (in pixels): 144 x 144

In Sencha Touch, we define the app’s icons with the help of three configuration options of the Application Class – icon, phoneIcon, and tabletIcon.

[Read more...]

How to Create a Sencha Touch 2 App, Part 1

In this series we will create the Sencha Touch 2 version of the Notes Application, an application that allows its users to take notes and store them on the device running the app. Along the way, we will dive into the following areas:

  • The building blocks of a Sencha Touch application.
  • How to implement navigation in an application with multiple views.
  • How to edit data using Sencha Touch form elements.
  • How to render information using lists views.
  • How Sencha Touch stores data with HTML5 local storage.

In the first part of the series, we are going to define the features of the application, its look and feel, and we will start building its main screen.

The Features Of The Notes App

We want our app to give its users the following abilities:

  • Create notes.
  • Edit existing notes.
  • Delete notes.
  • Render a list of the notes currently saved on the device.
  • Persist notes on the device running the application, across browser sessions.

Sencha Touch or jQuery Mobile? – Read This Before You Make a Decision

If you ever need to choose between jQuery Mobile and Sencha Touch, you need to consider these factors before making your decision:

About jQuery Mobile

  • It’s a UI-only library, which relies on jQuery and jQuery UI for DOM manipulation, Ajax and other utilities
  • To create UI widgets, you generally need to hand-code their html, and the library enhances their look and feel
  • As it works by enhancing the html you create, it allows you to re-use or re-purpose existing html
  • Some simple applications can be created using only html, without having to write JavaScript code
  • It has a relatively small object model, which makes it faster and easier to learn, specially if you are familiar with jQuery
  • It does not impose a coding discipline or structure, which gives you flexibility, but can lead to applications that are difficult to maintain
  • Easier to integrate with other frameworks
  • Targets more devices than Sencha Touch
  • Not tied to a particular vendor

About Sencha Touch

  • It’s a library that tries to do it all: UI widgets, DOM manipulation, Ajax and other utilities
  • Does not depend on other libraries
  • Follows a JavaScript-centric approach, where you are required to write little html
  • Has a large object model, which provides more features out of the box, but takes longer to learn
  • Imposes a coding structure and discipline, which generally results in well-organized code
  • Provides built-in server and local storage abstractions, which make it easier to perform CRUD operations on relational data
  • Has built-in facilities for creating iOS and Android native packages
  • Targets less devices than jQuery Mobile

Want To Learn More?

I have a selection of Sencha Touch and jQuery Mobile tutorials that will help you build great applications. Check them out using these links:

New eBook – Building a Sencha Touch Application

Book: Building a Sencha Touch Application

I just published the Building a Sencha Touch Application eBook. The book will teach you how to create a Sencha Touch 1.1.1 application that allows its users to take notes and store them on the device running the app.

You can preview the book using this link: Preview Ebook – Building a Sencha Touch Application.

I would like to thank Ted JenkinsAlex Blount, James Brooks and Hector Iribarne for their feedback and help creating this book. Thank you, Ted, Alex, James and Hector.

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.