Todo sobre la manzana y más

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.

The Application’s User Interface

The main screen of the Notes App will render a list of the existing notes. We will name this View NotesListContainer. Here is its mockup, depicting the Sencha Touch Components we will use to build it:

As you can see, the NoteListContainer View is an Ext.Container Component that hosts a Toolbar Component and a List Component. We will treat the Ext.Lit as a separate View – the NotesList View.

A second screen, the NoteEditor View, will allow users to create, edit and delete notes. This view will look just like the following mockup:

The NoteEditor View is an Ext.form.Panel Component that contains a couple of Toolbars and the form elements needed to edit a note’s properties.

We also need a mechanism for our users to navigate to these screens. As each Sencha Touch Application gets a Viewport instance upon launch, we will use the Viewport to take care of rendering the NotesListContainer and NoteEditor Views, as well as manage the navigation between them:

The Viewport is ideal for this task. It extends from the Container Class, and it defaults to a full screen Card layout, which is what we need in the Notes App.

Organizing A Sencha Touch Application In Directories And Files

With the application features and user interface already defined, we can begin writing the source code. We will organize our source code in a directories tree like this:

We will place our files under the NotesApp directory, with the Application’s setup code in the app.js file.

We will also have an app directory, in which we will have the controller, model, profile, store, and view directories. The controllers, models, profiles, stores and views used in the Application will reside in these directories.

The index.html file will launch the app. In it, we will include the Sencha Touch framework, along with our application:

Creating a Sencha Touch Application Instance

Our first step in the app.js file will consist of creating an instance of the Sencha Touch Application Class. We are going to define an Application instance as follows:

1

Ext.application({

2

name: "NotesApp",

3

launch: function() {

4

5

console.log("App launch");

6

}

7

});

The application() method loads an Application instance after the page is ready, and it uses the supplied config object to configure the app. In our case, we are simply giving the app a name, and defining the launch() function.

One important role of the application() function is to trigger the load of any Models, Views, Controllers, Stores and Profiles the application depends on. This happens automatically when we define these dependencies using the models, views, controllers, stores and profiles config objects. We will see this feature in detail in this tutorial.

The launch() function is invoked as soon as the app loads its dependencies and instantiates its Controllers.

If we navigate to this page on the emulator, we should see something like this:

Extending Classes In Sencha Touch

We already know that the main screen of our application will render the list of notes currently cached on the device. To build this screen, we will use an instance of the Container Class, which will host a Toolbar and a List:

First, we will create the NotesListContainer.js file in the view directory:

In the NotesListContainer.js file, we will then define the NotesListContainer View like so:

1

Ext.define("NotesApp.view.NotesListContainer", {

2

extend: "Ext.Container",

3

config: {

4

items: [{

5

xtype: "toolbar",

6

docked: "top",

7

title: "My Notes",

8

items: [{

9

xtype: "spacer"

10

}, {

11

xtype: "button",

12

text: "New",

13

ui: "action",

14

id:"new-note-btn"

15

}]

16

}]

17

}

18

});

Here we are using Ext.define() to define an extension to the Ext.Container class. We have added a Toolbar instance to this view, docked to the top. The Toolbar will in turn contain one Button, the New button, which will allow our users to activate the NoteEditor View and create a new note.

Pay attention to how we included a Spacer Component before the button. This will place the New button on the right end of the Toolbar. Also, how the ui=”action” config allows us to give the button a distinctive look, indicating that it represents the default button on the view.

Specifying Application Dependencies In Sencha Touch

With the NotesListContainer Class defined, it is time to make the Application aware of it. Back in the app.js file, we are going to add the views config to the application() function:

We inform the Application that it has a dependency on the NoteListContainer View by using the views config:

1

views: ["NotesListContainer"]

The Application Class expects its models, views, controllers, stores and profiles to exist in the app/model, app/view, app/controller, app/store, and app/profile directories. With this convention, we can define models, views, controllers, stores and profiles using the last part of their names, as we do here. If we use a different directory structure, we will need to refer to them using their fully qualified names.

Time to check out how the View looks. On the emulator, you should see something similar to this:

Very nice, right? Let’s move on to the Controller. We need a Controller Class to be in charge of handling user input, modifying the models, and governing the transitions between views.

Creating Sencha Touch Controllers

Let’s define a very simple Notes Controller Class. We will place this Class in the Notes.js file, which we will add to the controller directory:

And this is the Controller’s definition:

1

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

2

extend: "Ext.app.Controller",

3

launch: function() {

4

this.callParent();

5

console.log("launch");

6

},

7

init: function() {

8

this.callParent();

9

console.log("init");

10

}

11

});

Controllers contain a couple of functions, init() and launch(), that run at different moments during the Application’s startup process. The init() function is invoked by the framework before the Application’s launch() function. The Controller’s launch() function is invoked after the Application’s launch() function runs.

We will make the Application aware of the Notes Controller Class by adding this Controller to the app’s controllers config:

As we already know, having the Notes Controller Class listed in the controllers config will cause the Application to automatically instantiate and keep a reference to this Controller.

How To Handle View Events In a Sencha Touch Controller

At this point we have our Controller set up, and we are ready to start adding features to it. The first thing the Notes Controller needs to do is handle taps on the New button. We can accomplish this by making the following modification to the Controller’s definition:

1

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

2

extend: "Ext.app.Controller",

3

config: {

4

refs: {

5

newNoteBtn: "#new-note-btn"

6

},

7

control: {

8

newNoteBtn: {

9

tap: "onNewNote"

10

}

11

}

12

},

13

onNewNote: function() {

14

console.log("onNewNote");

15

}

16

17

// init and launch functions omitted.

18

});

Notice the change? Exactly – the refs and control configurations. They are the foundation of the mechanism by which a Controller acquires references to the Components in the app, and defines event handlers for them.

Refs give the Controller the ability to find Components in the app. They use the ComponentQuery Class, which retrieves components using a CSS selector-like syntax. I will not go into the details of ComponentQuery in this article, but I highly recommend that you take some time to study it.

In the Notes Controller, we use the newNoteBtn ref to create a reference to the Component whose id is #new-note-btn. This is the New button.

1

refs: {

2

newNoteBtn: "#new-note-btn"

3

}

Based on this ref, the framework generates a getter function that we can use to work with the New button reference if we need to. Ref-derived getter functions are named following a simple format, consisting of the word get and the capitalized name of the ref in question. In our case, the name of the function will be getNewNoteBtn().

Refs also give us the ability to instantiate the components they reference if such components do not already exist. Although we are not taking this approach in our app, I recommend that you study these features. They are important, especially if you will be building large applications where you cannot afford or do not need to instantiate all the application’s components upon application launch.

The control config allows us to define event handlers for any of the Application’s Components. We can use refs or ComponentQuery selectors to define event handlers within control. In the Notes Controller, we are using the newNoteBtn ref to define a tap handler for the New button:

1

control: {

2

newNoteBtn: {

3

tap: "onNewNote"

4

}

5

}

Obviously, we also need to define the handler function, which we do further down in the Controller’s source:

1

onNewNote: function() {

2

console.log("onNewNote");

3

}

The onNewNote() function is simply writing a message to the console, but we will soon add to it the code that will open the NoteEditor View.

Let’s take a moment to check the application on the emulator. In particular, what happens when we tap the New button. If a tap occurs, the console output should display the message we just added in the onNewNote() handler:

One important takeaway from this tutorial is that when you need your Controller to handle a particular event on a Component, you can follow these steps:

Create a ref for the Component.

Create an entry for the ref in the Controller’s control config.

Use the entry in the control config to specify the handler for the events in which you are interested.

Summary

We are going to end this first part of the tutorial taking a minute to recap what we have done so far.

After defining the features and the user interface of the application we will build, we started to become familiar with how Sencha Touch works. We learned how to create the directories for the app, and how to create an instance of the Application Class.

We also created the first version of the Application’s main View, which we named NotesListContainer, and started working on the Notes Controller instance that will manage the interactions between the different parts of the Application.

In the next chapter of this series we will continue working on the Application’s main View and its child elements, as we need to create the List that will render the notes cached on the device.