How to Create a Sencha Touch 2 App, Part 5

http://miamicoder.com/2012/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:

1 Ext.define("NotesApp.view.NotesList", {
2     extend: "Ext.Container",
3     requires:"Ext.dataview.List",
4     alias: "widget.noteslistview",
5
6     config: {
7         layout: {
8             type: 'fit'
9         },
10         items: [{
11             xtype: "toolbar",
12             title: "My Notes",
13             docked: "top",
14             items: [
15                 { xtype: 'spacer' },
16                 {
17                     xtype: "button",
18                     text: 'New',
19                     ui: 'action',
20                     itemId: "newButton"
21                 }
22             ]
23         }, {
24             xtype: "list",
25             store: "Notes",
26             itemId:"notesList",
27             loadingText: "Loading Notes...",
28             emptyText: '<div>No notes found.</div>',
29             onItemDisclosure: true,
30             grouped: true,
31             itemTpl: '<div>{title}</div><div>{narrative}</div>'
32         }],
33         listeners: [{
34             delegate: "#newButton",
35             event: "tap",
36             fn: "onNewButtonTap"
37         }, {
38             delegate: "#notesList",
39             event: "disclose",
40             fn: "onNotesListDisclose"
41         }]
42     },
43     onNewButtonTap: function () {
44         console.log("newNoteCommand");
45         this.fireEvent("newNoteCommand", this);
46     },
47     onNotesListDisclose: function (list, record, target, index, evt, options) {
48         console.log("editNoteCommand");
49         this.fireEvent('editNoteCommand', this, record);
50     }
51 });

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:

1 {
2     delegate: "#newButton",
3     event: "tap",
4     fn: "onNewButtonTap"
5 }

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:

1 {
2     delegate: "#notesList",
3     event: "disclose",
4     fn: "onNotesListDisclose"
5 }

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:

1 Ext.define("NotesApp.view.NoteEditor", {
2     extend: "Ext.form.Panel",
3     requires: "Ext.form.FieldSet",
4     alias: "widget.noteeditorview",
5     config: {
6         scrollable: 'vertical',
7         items: [
8             {
9                 xtype: "toolbar",
10                 docked: "top",
11                 title: "Edit Note",
12                 items: [
13                     {
14                         xtype: "button",
15                         ui: "back",
16                         text: "Home",
17                         itemId: "backButton"
18                     },
19                     { xtype: "spacer" },
20                     {
21                         xtype: "button",
22                         ui: "action",
23                         text: "Save",
24                         itemId: "saveButton"
25                     }
26                 ]
27             },
28             {
29                 xtype: "toolbar",
30                 docked: "bottom",
31                 items: [
32                     {
33                         xtype: "button",
34                         iconCls: "trash",
35                         iconMask: true,
36                         itemId: "deleteButton"
37                     }
38                 ]
39             },
40             { xtype: "fieldset",
41                 items: [
42                     {
43                         xtype: 'textfield',
44                         name: 'title',
45                         label: 'Title',
46                         required: true
47                     },
48                     {
49                         xtype: 'textareafield',
50                         name: 'narrative',
51                         label: 'Narrative'
52                     }
53                 ]
54             }
55         ],
56         listeners: [
57             {
58                 delegate: "#backButton",
59                 event: "tap",
60                 fn: "onBackButtonTap"
61             },
62             {
63                 delegate: "#saveButton",
64                 event: "tap",
65                 fn: "onSaveButtonTap"
66             },
67             {
68                 delegate: "#deleteButton",
69                 event: "tap",
70                 fn: "onDeleteButtonTap"
71             }
72         ]
73     },
74     onSaveButtonTap: function () {
75         console.log("saveNoteCommand");
76         this.fireEvent("saveNoteCommand", this);
77     },
78     onDeleteButtonTap: function () {
79         console.log("deleteNoteCommand");
80         this.fireEvent("deleteNoteCommand", this);
81     },
82     onBackButtonTap: function () {
83         console.log("backToHomeCommand");
84         this.fireEvent("backToHomeCommand", this);
85     }
86
87 });

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:

1 listeners: [
2     {
3         delegate: "#backButton",
4         event: "tap",
5         fn: "onBackButtonTap"
6     },
7     {
8         delegate: "#saveButton",
9         event: "tap",
10         fn: "onSaveButtonTap"
11     },
12     {
13         delegate: "#deleteButton",
14         event: "tap",
15         fn: "onDeleteButtonTap"
16     }
17 ]

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:

1 Ext.application({
2     name: "NotesApp",
3
4     models: ["Note"],
5     stores: ["Notes"],
6     controllers: ["Notes"],
7     views: ["NotesList", "NoteEditor"],
8
9     launch: function () {
10
11         var notesListView = {
12             xtype: "noteslistview"
13         };
14         var noteEditorView = {
15             xtype: "noteeditorview"
16         };
17
18         Ext.Viewport.add([notesListView, noteEditorView]);
19
20     }
21 });

Modifying the Controller

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

1 Ext.define("NotesApp.controller.Notes", {
2
3     extend: "Ext.app.Controller",
4     config: {
5         refs: {
6             // We're going to lookup our views by alias.
7             notesListView: "noteslistview",
8             noteEditorView: "noteeditorview",
9             notesList: "#notesList"
10         },
11         control: {
12             notesListView: {
13                 // The commands fired by the notes list container.
14                 newNoteCommand: "onNewNoteCommand",
15                 editNoteCommand: "onEditNoteCommand"
16             },
17             noteEditorView: {
18                 // The commands fired by the note editor.
19                 saveNoteCommand: "onSaveNoteCommand",
20                 deleteNoteCommand: "onDeleteNoteCommand",
21                 backToHomeCommand: "onBackToHomeCommand"
22             }
23
24         }
25     },
26     // Transitions
27     slideLeftTransition: { type: 'slide', direction: 'left' },
28     slideRightTransition: { type: 'slide', direction: 'right' },
29
30     // Helper functions
31     getRandomInt: function (min, max) {
32         return Math.floor(Math.random() * (max - min + 1)) + min;
33     },
34     activateNoteEditor: function (record) {
35
36         var noteEditorView = this.getNoteEditorView();
37         noteEditorView.setRecord(record); // load() is deprecated.
38         Ext.Viewport.animateActiveItem(noteEditorView, this.slideLeftTransition);
39     },
40     activateNotesList: function () {
41         Ext.Viewport.animateActiveItem(this.getNotesListView(), this.slideRightTransition);
42     },
43
44     // Commands.
45     onNewNoteCommand: function () {
46
47         console.log("onNewNoteCommand");
48
49         var now = new Date();
50         var noteId = (now.getTime()).toString() + (this.getRandomInt(0, 100)).toString();
51
52         var newNote = Ext.create("NotesApp.model.Note", {
53             id: noteId,
54             dateCreated: now,
55             title: "",
56             narrative: ""
57         });
58
59         this.activateNoteEditor(newNote);
60
61     },
62     onEditNoteCommand: function (list, record) {
63
64         console.log("onEditNoteCommand");
65
66         this.activateNoteEditor(record);
67     },
68     onSaveNoteCommand: function () {
69
70         console.log("onSaveNoteCommand");
71
72         var noteEditorView = this.getNoteEditorView();
73
74         var currentNote = noteEditorView.getRecord();
75         var newValues = noteEditorView.getValues();
76
77         // Update the current note's fields with form values.
78         currentNote.set("title", newValues.title);
79         currentNote.set("narrative", newValues.narrative);
80
81         var errors = currentNote.validate();
82
83         if (!errors.isValid()) {
84             Ext.Msg.alert('Wait!', errors.getByField("title")[0].getMessage(), Ext.emptyFn);
85             currentNote.reject();
86             return;
87         }
88
89         var notesStore = Ext.getStore("Notes");
90
91         if (null == notesStore.findRecord('id', currentNote.data.id)) {
92             notesStore.add(currentNote);
93         }
94
95         notesStore.sync();
96
97         notesStore.sort([{ property: 'dateCreated', direction: 'DESC'}]);
98
99         this.activateNotesList();
100     },
101     onDeleteNoteCommand: function () {
102
103         console.log("onDeleteNoteCommand");
104
105         var noteEditorView = this.getNoteEditorView();
106         var currentNote = noteEditorView.getRecord();
107         var notesStore = Ext.getStore("Notes");
108
109         notesStore.remove(currentNote);
110         notesStore.sync();
111
112         this.activateNotesList();
113     },
114     onBackToHomeCommand: function () {
115
116         console.log("onBackToHomeCommand");
117         this.activateNotesList();
118     },
119
120     // Base Class functions.
121     launch: function () {
122         this.callParent(arguments);
123         var notesStore = Ext.getStore("Notes");
124         notesStore.load();
125         console.log("launch");
126     },
127     init: function () {
128         this.callParent(arguments);
129         console.log("init");
130     }
131 });

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