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.

Creating a Form Panel in Sencha Touch

We will place the View’s source code in the  NoteEditor.js file, which we will create in the view directory:

In the file, we will define an empty NoteEditor Class like so:

Ext.define("NotesApp.view.NoteEditor", {
    extend: "Ext.form.Panel",
    requires: "Ext.form.FieldSet",
    alias: "widget.noteeditor",
    config:{
        scrollable:'vertical'
    },
    initialize: function () {

        this.callParent(arguments);

    }
});

The Note Editor is an extension of the Ext.form.Panel Class. As we will use a FieldSet Class instance in the View, we are asking the loader to download its source by using the requires config. We are also using the scrollable config to allow the contents of the Panel to scroll vertically. This stops the form from being cropped when its height is larger than the screen height of the device.

As we did with the Notes ListContainer Class, we are going to use the initialize() function to define the Note Editor’s components:

Ext.define("NotesApp.view.NoteEditor", {
    extend: "Ext.form.Panel",
    requires: "Ext.form.FieldSet",
    alias: "widget.noteeditor",
    config:{
        scrollable:'vertical'
    },
    initialize: function () {

        this.callParent(arguments);

        var backButton = {
            xtype: "button",
            ui: "back",
            text: "Home"
        };

        var saveButton = {
            xtype: "button",
            ui: "action",
            text: "Save"
        };

        var topToolbar = {
            xtype: "toolbar",
            docked: "top",
            title: "Edit Note",
            items: [
                backButton,
                { xtype: "spacer" },
                saveButton
            ]
        };

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

        var bottomToolbar = {
            xtype: "toolbar",
            docked: "bottom",
            items: [
                deleteButton
            ]
        };

        var noteTitleEditor = {
            xtype: 'textfield',
            name: 'title',
            label: 'Title',
            required: true
        };

        var noteNarrativeEditor = {
            xtype: 'textareafield',
            name: 'narrative',
            label: 'Narrative'
        };

        this.add([
            topToolbar,
            { xtype: "fieldset",
                items: [noteTitleEditor, noteNarrativeEditor]
            },
            bottomToolbar
        ]);
    }

});

Within initialize(), after invoking callParent(), we proceed to define the top Toolbar, along with its two buttons, the Home Button and the Save Button. We then define the bottom Toolbar and the Delete Button.

The noteTitleEditor and noteNarrativeEditor are the fields we will use to edit the note’s title and narrative. They are instances of the Ext.form.Text and Ext.form.TextArea Classes.

Once all the View’s components are defined, we proceed to add them to the View. This is where we also add an Ext.form.FieldSet instance to enhance the appearance of the form. The title and narrative editors will render within the FieldSet:

this.add([
    topToolbar,
    { xtype: "fieldset",
        items: [noteTitleEditor, noteNarrativeEditor]
    },
    bottomToolbar
]);

Rendering a View In Sencha Touch

Before we start developing the features of the Note Editor, we are going to work on the code that will render this View when the New Button in the Notes List Container is tapped.

In the previous chapter of this tutorial we created the tap handler for the New Button, which fires the newNoteCommand event. We also added the onNewNoteCommand() listener to the Notes Controller. We will use this function to activate the Note Editor View.

To activate the Note Editor View in the Controller, we first need to acquire a reference to the View. We will use the noteeditor ref for this purpose:

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

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

   // Remainder of the controller’s code omitted for brevity.

});

Remember, this ref automatically creates a getNoteEditor() function in the controller, which we can use to refer to the NoteEditor instance and make it the active View in the application.

Next, we need to modify the onNewNoteCommand() function:

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

Here things get more interesting. We are creating a new note, and passing it to the activateNoteEditor() function.

We are going to use the getRamdomInt() helper function to generate the unique id for a note:

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

The activateNoteEditor() function will load the new note into the Note Editor, and make the Editor active:

activateNoteEditor: function (record) {

    var noteEditor = this.getNoteEditor();
    noteEditor.setRecord(record); // load() is deprecated.
    Ext.Viewport.animateActiveItem(noteEditor, this.slideLeftTransition);
}

Here we are taking advantage of the Ext.form.Panel’s setRecord() function, which allows us to load the values of a model instance into the form’s fields whose names match those of the model’s fields.

This function also uses the slideLeftTransition variable, which we need to define like so:

slideLeftTransition: { type: 'slide', direction: 'left' }

The transition will bring the Note Editor into view with a slide motion from the right to the left.

This is all the Controller needs to do in order to activate the Note Editor View when the New Button is tapped. However, we need to make the application aware of the NoteEditor Class.

Making The Application Aware Of A View

In the app.js file, we will add the new View to the views config:

views: ["NotesList", "NotesListContainer", "NoteEditor"]

We are also going to instantiate the View in the Application’s launch function:

Ext.application({
    name: "NotesApp",

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

    launch: function () {

        var notesListContainer = {
            xtype: "noteslistcontainer"
        };
        var noteEditor = {
            xtype: "noteeditor"
        };

        Ext.Viewport.add([notesListContainer,noteEditor]);
    }
});

Ready to check it out? Well, start the emulator. :-)

A tap on the New Button should render the Note Editor View:

Editing A Record Rendered In A Sencha Touch List

We also want to activate the Note Editor View when a note’s disclosure button is tapped:

In order to accomplish this, we need to revisit the onEditNoteCommand() function in the Controller, and add the code that will activate the NoteEditor:

onEditNoteCommand: function (list, record) {

    console.log("onEditNoteCommand");

    this.activateNoteEditor(record);
}

Incredibly simple thanks to the fact that the disclose event supplies the selected Note model instance to the handler through the record argument. All we need to do here is call activateNoteEditor(), which we created a few minutes ago.

Back in the emulator, we should be able to see the Note Editor render the selected note:

Now it is time to save the note. We need to take care of a few things in order for this to happen.

Using A LocalStorage Proxy In Sencha Touch

First, we need to stop using hard-coded notes as the data for the Notes store.

Up to this point, we have been using the data config to define a few hard-coded records in the store:

Ext.define("NotesApp.store.Notes", {
    extend: "Ext.data.Store",
    config: {
        model: "NotesApp.model.Note",
        data: [
            { title: "Note 1", narrative: "narrative 1" },
            { title: "Note 2", narrative: "narrative 2" },
            { title: "Note 3", narrative: "narrative 3" },
            { title: "Note 4", narrative: "narrative 4" },
            { title: "Note 5", narrative: "narrative 5" },
            { title: "Note 6", narrative: "narrative 6" }
        ],
        sorters: [{ property: 'dateCreated', direction: 'DESC'}]
    }
});

As we intend to cache notes on the device that runs the app, we are going to discontinue the data config in the store, and define a LocalStorage proxy as follows:

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

LocalStorageProxy uses the HTML5 localStorage API to save Model data on the client browser. This proxy is ideal for storing multiple records of similar data. And it requires that we provide the id config, which is the key that will identify our data in the localStorage object.

Now the Notes store has the ability to save and read data from localStorage, and we can go back to the Views and Controller to take care of the functions that will save the data.

Adding Event Listeners To A Sencha Touch Controller

In the NoteEditor Class, let’s modify the saveButton declaration in the initialize() function like so:

var saveButton = {
    xtype: "button",
    ui: "action",
    text: "Save",
    handler: this.onSaveButtonTap,
    scope: this
};

The handler config defines the handler function that will run when a user taps the button. To make sure we run the handler in the scope of the NoteEditor, we set the scope config to this.

Now we can define onSaveButtonTap() as follows:

onSaveButtonTap: function () {
    console.log("saveNoteCommand");
    this.fireEvent("saveNoteCommand", this);
}

No surprises here, right? As we’ve done with other handlers, we are capturing the Button tap event within the View and defining a View event, saveNoteCommand.

As we already know, the fact that the NoteEditor View broadcasts the saveNoteCommand event is not enough for the Controller to be able to listen to it. We need to tell the Controller where this event is coming from, which we did when we added the noteEditor entry in the refs config of the Controller:

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

We will use the Controller’s control config to map the saveNoteCommand event to a handler function. Therefore, need an entry for the Note Editor in the control config:

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

Finally, we will define the onSaveNoteCommand() function like so:

onSaveNoteCommand: function () {

    console.log("onSaveNoteCommand");

    var noteEditor = this.getNoteEditor();

    var currentNote = noteEditor.getRecord();
    var newValues = noteEditor.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();
}

We begin onSaveNoteCommand() acquiring references to the note being edited and the values in the form’s fields:

var noteEditor = this.getNoteEditor();
var currentNote = noteEditor.getRecord();
var newValues = noteEditor.getValues();

Then, we transfer the new values to the loaded note:

currentNote.set("title", newValues.title);
currentNote.set("narrative", newValues.narrative);

Model Validation In Sencha Touch

Next comes an important part, which is validation. To validate the new values we loaded into the model instance, we first call the model’s validate() function, and then call the isValid() function on the errors object returned by validate():

var errors = currentNote.validate();

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

The Ext.data.Model’s validate() function iterates over the validations defined for the model, and returns an Ext.data.Errors instance containing Ext.data.Error instances for each model field that is invalid.

In our case, the only field with an attached validator is the note’s title. When we find that the model is invalid, we first display an alert using the validator’s configured message, and then call the model’s reject() function. This function reverts the modified fields back to their original values before we exit the onSaveNoteCommand() handler.

Saving Data Using LocalStorageProxy

In onSaveNoteCommand(), after confirming that the modified note is valid, we move on to save it on the device:

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

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

notesStore.sync();

As this routine works for new or edited notes, we need to find out if the note is new by searching the store using its findRecrod() function. If the note is new, we add it to the store.

The store’s sync() function asks the store’s proxy to process all the changes, effectively saving new or edited notes, and removing deleting notes from localStorage.

After the store’s records have been updated, we sort them by date:

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

Returning To The Main View

Our last step in onSaveNoteCommand() consists of invoking the activateNotesList() function. This is a helper function, similar to activateNoteEditor(), that will make the app’s main View active:

activateNotesList: function () {
    Ext.Viewport.animateActiveItem(this.getNotesListContainer(), this.slideRightTransition);
}

In this case we are using a right-slide transition, which we will define like so:

slideRightTransition: { type: 'slide', direction: 'right' }

OK. What do you think about doing another quick check? This time we should be able to save a note:

Summary

In this article we added a couple of features to the Notes App: the ability to create notes, and edit existing notes.

We started by learning how to use Sencha Touch’s Form components to edit data in an application, and how to use a LocalStorageProxy instance to cache data on the device running the application. We also learned how to validate a Sencha Touch data Model, synchronize a Data Store with its Proxy, and revert Model changes when its data is invalid.

In addition, we learned how to create transitions and connect different Views in a multi-view application.

At this point the application is missing the ability to delete notes. We will add this feature in the next part of this tutorial, where we will also modify the Notes List so it renders the cached notes grouped by date.

Stay tuned!

Downloads

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

The Entire Series

Want To Learn More?

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

Comments

  1. Lance says

    Hey Jorge,

    Thanks for these tutorials, they’re great. Definitely staying tuned.

    The downloadable source seems to not have the handlers etc for the back button. I was able to add it in from your tut’s though so thanks again!

    Any chance in the future you’d consider showing how to pull the notes in from an external source?

    • says

      Thank you, Lance. I am planning to add the handlers for the Back button in the next chapter of the tutorial. I’m glad to see that you already added them. :-)

  2. BrownieBoy says

    You should be aware that Apple has deliberately broken HTML5 localStorage for webview-based apps under iOS 5.1. HTML5 localStorage is now treated as temporary by iOS 5.1. It may be (and often is) deleted from the device at any time, so rendering it all but useless for the task for which it is intended.

    Note that this problem affects webviews only. LocalStorage still works fine if you’re running your code in a browser such as Mobile Safari. But if you’re packing up your HTML5/JavaScript/CSS with tools like Sencha, PhoneGap or Titanium so that you can put it up onto the App Store then you’re going to run smack, bang into this problem, sooner rather than later. The PhoneGap guys reportedly have a patch available already, but there’s no solution from Sencha as yet. (Although I guess you could use PhoneGap to package your Sencha code, assuming the former’s patch does actually work.)

    See Sencha’s own report of this problem: http://www.sencha.com/blog/html5-scorecard-the-new-ipad-and-ios-5-1

    • says

      Thank you, BrownieBoy. I’m aware of the issue. It’s good that you brought it up, though.

      BTW – PhoneGap 1.6 includes a fix for this bug caused by the iOS 5.1 update.

  3. says

    Thanks for the new post! ^^
    I’ve successfully implemented.

    But I had some problems with the method setRecord.
    The fact is that instead of using your way of creating the editor I’ve used the component Ext.navigation.View.

    I make the creation of the editor in this way:
    showNoteEditor: function (record) {
    var editorView = {
    xtype: “notesdetail”,
    record: record
    };

    this.getMain().push(editorView);
    },

    main is my main view (Ext.navigation.View) -> refs: { … main: “mainpanel”, .. }

    I try using setRecord or using the config record, but nothing happen.
    The problem was that the view was not created yet.
    So the “record” was sent correctly, but fields aren’t initialized.

    I solved the problem by adding this in the initialize function of the editor.

    this.setRecord(this.getRecord());

    Thank you very much for the tutorial. I hope my contribution will serve ;)

  4. David says

    Great tutorials, but in this part of the tutorial, I have some problems to load de data from the localstorage, it throws me an error like this: “Uncaught TypeError: Cannot read property ‘isModel’ of undefined”. Any idea of what can I do to solve this problem? Thanks!

  5. says

    Hi Jorge,

    It is a nice tutorial it is very handy. i have added the back button but i am confusing how i add functionality to delete the record. can you please add this one.

    thanks
    waheed

      • says

        thanks for your immediate reply.

        i have added this function in the NoteEditer.js page by adding handler to the button handler: this.onDeleteButtonTap

        onDeleteButtonTap: function (list, record) {
        this.fireEvent(“deleteButtonCommand”, this, record);
        }

        adding deleteButtonCommand: “ondeleteButtonCommand” in the controller .

        ondeleteButtonCommand: function (list, record) {

        this.activateNotesList();
        }

        any suggestions…..

        • says

          I will add this feature in the next chapter. In the meantime, remove the list and record arguments from the onDeleteButtonTap handler. That’s not the right signature for the event. This event is fired by the button, not the list.

          • waheed says

            thanks you very much. you are rock star.

            i have delete the record easily with the modifications i have made. to remove the list and record arguments. and making this function in the controller.

            ondeleteButtonCommand: function () {

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

            if (notesStore.findRecord(‘id’, currentNote.data.id)) {
            notesStore.remove(currentNote);
            }

            notesStore.sync();
            this.activateNotesList();
            }

            now record is easily delete and go back to the list view of the project.

            i need further tutorials hmmm something like login system and crud examples. as i start my php coding with these example understandings. jorge can you please help me. i want to grasp over sencha touch soon and like a rock star like you.

            thanks
            waheed

      • says

        Hi jorge

        i have one issue in my example project. i have revise all of my work again but this issue was persist.

        Ext.getStore(“Notes”).load();

        this one is not working . if i comment this line then all things are good. but when i uncomment this line only loading image is displaying in the main page.

        and i seen this error
        Uncaught TypeError: Cannot read property ‘isModel’ of undefined

        could you please help me.

        thanks in advance.

          • waheed says

            it is working now.

            what does it mean. the app problem or sencha’s localdatabase problem.

        • CP3 says

          The problem happens at notesStore.sync(), once the note is removed, it change the local Storage file, The next you load it to you browser, the fault will appears by stop at the loading step. So i think it’s the API’s fault of ST, or we make something wrong when change the local Storage file…

  6. Lance says

    I’m getting the same “Uncaught TypeError: Cannot read property ‘isModel’ of undefined” error as the others. Flushing the cache does fix it, is that the only way to fix this?

  7. Doug says

    Hi Jorge –

    Nice tutorial – it’s a no-nonsense approach towards explaining how ST-2’s MVC pattern can be used.

    I’ve found that the ST-2 lib required by the app is sencha-touch-all-debug.js. Using the version specified in the demo app’s index.html file seems to be short some functionality; specifically “event/Dispatcher.js” and “event/publisher/Dom.js”.

    Thanks for the great details.

  8. Greg says

    Hi, Thank you for this amazing tutorial !!
    I have some trouble with data recovering. When i reload the page, my records are lost…
    I tried it in Chrome (last version) and iOS simulator, both with the same issue.

  9. milica says

    Hi again!
    I have an issue with the validation, when I try to save note without title I get this log:
    [NotesApp.controller.Notes#onSaveNoteCommand] Using Ext.Msg without requiring Ext.MessageBox
    What am I doing wrong? :(

  10. Vincent says

    Great post!
    I used this article to learn ST2. Thanks a lot!
    BTW, can you tell me which IDE and Emulator you used to develop?

    • says

      I’ve been using notepad++ for editing JavaScript code. I also use JSLint and Visual Studio to validate code. I use the Android emulator from the Android SDK and the iPhone emulator that comes with Xcode.

  11. Lin says

    many thanks, the example works well
    I have been trying to use json file for storing notes instead of local storage. my problem is i am able to load records to show on novelist but not able to add new & update.
    Could you kindly demonstrate using json file rather than local storage in your examples?

    • says

      You will need to define a server-side handler that will update your json file. I’m planning on posting examples with server-side code in the future.

      • says

        Hello Jorge,

        I am using proxy ajax and data is saved in somefile.json.

        I have server side script ready to return json. But dont know how to update the data in somfile.json when device is conneted to internet.

        Please provide example. It would be really helpful.

  12. Tina says

    Hey, greate Tutorials!
    I’m always expanding them to learn more and now I’m stuck with a Problem. Is it possible to have a dynamic height at this Point: (in view/NoteEditor)
    var noteNarrativeEditor = {
    xtype: ‘textareafield’,
    name: ‘narrative’,
    label: ‘Narrative’
    };
    I know, there is the the property “height”. But I want it to fit the content, so I can’t set it with a static amount of Pixel. I already googled some time but can’t find a solution and I can’t believe that there is none.. it’s so basic.
    Thanks a lot! Tina

    • says

      Tina, I think you can use setHeight(). You would need to calculate the available height, and then set the height of the text area.

      This is a very small change in a view, but for cases where there are large differences in a View depending on device, etc., you might want to consider creating different device profiles.

  13. Tina says

    Thanks for reply. I want to set the Size of the textareafield depending on the Content, not on the rest of free space on the Display. I just don’t know how I can calculate the needed rows/height.
    One of the Problems is, that I added also an ‘Edit-Button’ and when you open one Note it’s first ReadOnly, I cannot scroll in the texarea then. Thats why I want to expand the textarea, that the whole text is shown.

    • says

      Tina, I would use one additional View to render the note in read-only mode. So the Views would be: Notes List -> Note (Read-Only) -> Note (Edit). The read-only View can be just a container with a template, for example.

  14. waheed says

    Hi,

    i like your structure of work.

    i am developing an app from scratch. i have store and model instead of narrative to description.

    in controller i have a function to save the comment.. please see.

    saveCommentCommand: function () {

    var now = new Date();
    var commentId = now.getTime().toString();
    var imgForm = this.getImageForm();

    //var currentComment = imgForm.getRecord();
    var newValues = imgForm.getValues();
    var newComment = Ext.create(“Tweet.model.Comment”, {
    id: commentId,
    dateCreated: now,
    title: newValues.title,
    description: newValues.description
    });

    //console.log(newValues.title);
    }

    this function is not save the comment.

    i have loaded the model in the controller launch function. like this
    Ext.getStore(“Comment”).load();
    but causing this error.
    Cannot call method ‘load’ of undefined

    Please give me suggestion

      • waheed says

        yes i have defined. now i have another issue
        saveCommentCommand: function () {

        var now = new Date();
        var commentId = now.getTime().toString();
        var imgForm = this.getImageForm();
        var newValues = imgForm.getValues();

        var newComment = Ext.getStore(“Comment”);
        newComment.add({
        id: commentId,
        dateCreated: now,
        title: newValues.title,
        description: newValues.description
        });

        newComment.sync();
        imgForm.reset();
        //Ext.Viewport.animateActiveItem(this.getCommentList(), this.slideLeftTransition);
        }

        with this function data is store and listing shows on the browser. but when i refresh the page all were gone?

  15. says

    Hi Jorge,

    first of all I want to say, that you made a great tutorial here. It makes it easy to understand the principles of ST 2.
    Now I’m not sure if anybody else does have this error. But when I try to run this example I get an error saying “Cannot call method ‘setRecord’ of undefined” in noteEditor.setRecord(record) in the activateNoteEditor function. The same error occurs when I use your downloadable code and also after clearing the browser cache.

    Do you have any quick hints for me?

    Thanks a lot in advance!

    • says

      ok…problem solved. It depends on the order declaring the models, stores, controllers and views.

      sorry for the inconvenience.

      Keep it up!

      • Ask says

        Hi Cliff,

        I am getting the same error.
        Could you please help me with explaining what should be the order ?

        Thanks in advance for your help.

        • ToRoll says

          Hi folks,
          I also struggled some time with this error.
          In the end I found that I “just” forgot to add the view to the Viewport in app.js
          as described in section “Making The Application Aware Of A View”.
          (I didn’t see this, because I used a slightly different notation).

          Adding the view via xtype ” to the Viewport solved this issue:
          Ext.Viewport.add({xclass: ‘widget.noteeditor’});

          Maybe this hint could be helpful for those who have run into the same trap like me.

          Greetings and thanks to Jorge by this way :-)

          • Steven says

            Exact dude, I had the same problem, but if i’m doing:

            launch: function () {
            Ext.Viewport.add({xclass: ‘widget.noteslistcontainer’});
            Ext.Viewport.add({xclass: ‘widget.noteeditor’});
            }

            Everything works ! :)

  16. Andrew says

    Hi Jorge,

    I’m new to sencha touch and wonder why you create all items in the initialize() function. Isn’t the “config possibility” one of the great new features of ST2? I watched the “Getting started with ST2″ video from the official website and there the author doesn’t use initialize().

    E.g. one of my views looks like this:

    Ext.define(“AraDBM.view.ContactForm”, {
    extend: ‘Ext.form.Panel’,
    xtype: ‘contactform’,

    config: {

    items: [

    {
    xtype: ‘fieldset’,
    defaults: {
    labelWidth: ‘60%’,
    clearIcon: false,
    },
    items: [
    {
    xtype: 'textfield',
    name: 'lastname',
    label: 'Nachname',
    },
    {
    xtype: 'textfield',
    name: 'firstname',
    label: 'Vorname'
    },
    ....
    ]
    },
    {
    xtype: ‘button’,
    text: ‘Suchen’,
    ui: ‘secondary’,
    }
    ]
    },
    });

    What’s the difference and why should i use your approach?

    Thanks in advance!

    • says

      Both approaches are valid. I used the initialize function to show that this is also possible. I don’t get any extra benefit from using config in this case, as the getItems() function already exists. By the way, the documentation also has examples using initialize.

  17. Alesha Mary Milred Fernandes says

    Hi Jorge,

    Have you worked on Maps in sencha,if so could you help me out with destroying an instance of the map and recreating it?? Thanking you.Alesha

  18. Tina says

    Hey Jorge,
    ich have an Problem. I dont know how to get the Record-Information besides when i make a textfield with name:’RecordValueName’. I tried it with tpl : ‘{RecordValueName}’ and all. But im not able to get it. Is there another way to get the RecordData except with textfiled and name?
    Thanks Tina

      • Tina says

        Well.. I made the whole Navigation like u did in ur Example. (Whole structure is the same)
        After disclosure I can get the Record Data in the new View like this:
        var country = {
        xtype : ‘textfield’,
        name : ‘Country’,
        label : ‘Country’
        };

        I get all Data like this, when i set the name of a textfield like the RecordDataValue. But when i want to use it differently, I dont know how to get them. For example like this:
        var allData= {
        xtype : ‘panel’,
        config : {
        styleHtmlContent : true,
        scrollable : ‘vertical’,
        tpl : ‘Country {Country}’
        }
        };

        I made so many examples, I already put the template in another file and with xtype and all but i just can’t get the Data in any other way than with ‘name’. Then there is just NO information at all.

        We commit the record like this in the Controller:

        var detail = this.getDetailContainer();
        detail.setRecord(record);

        Shouldnt there be a possibility in the DetailContainer to reached the RecordData with this.getRecord() or something?
        Thanks

  19. CP3 says

    Hi Jorge,
    I have a question, the function getNoteEditor() used in the controller(Notes.js) must has been defined, but i can’t find it.

    • JK says

      CP3,

      since Jorge hasn’t replied, I can answer this one. To activate a view from a controller, you create a ref to the view in the config. In this case, it’s

      config: {
      refs: { noteEditor: “noteeditor”}
      }

      this automatically creates a getter function, getNoteEditor. Look for the comment, “Remember, this ref automatically creates a getNoteEditor() function in the controller” early in this tutorial.

  20. Xavi says

    Hi Jorge,

    I have a problem when I click on the “Save” button:

    Uncaught TypeError: Cannot call method ‘getMessage’ of undefined …

    on this line code:

    Ext.Msg.alert(‘Wait!’, errors.getByField(“title”)[0].getMessage(), Ext.emptyFn);

    i’m very noob with javascript and sencha, and i can’t find the error… you can help me?

    Thx so much!

    • Net mat says

      i encountered the error “Uncaught TypeError: Cannot call method ‘getMessage’ of undefined ..”

      because I had put the quote on ‘noteid’ and ‘now’ fields in notes.js (controller) as shown below:

      var newNote = Ext.create(“NotesApp.model.Note”, {
      id: ‘noteId’,
      dateCreated: ‘now’,
      title: “”,
      narrative: “”
      });
      I changed it to:

      var newNote = Ext.create(“NotesApp.model.Note”, {
      id: noteId,
      dateCreated: now,
      title: “”,
      narrative: “”
      });
      as in the article as these are not string rather number. This solved the problem.
      Though, Its a reply to a year old post, i may help some one.
      Thanks Jorge for such nice tutorial.

  21. bee says

    Hi G, I am really impressed on your tutorial. very nice for beginners and this inspired me to create my own app. Thanks for that. Also when i was trying this application, it does not show date on the dates which appeasr for you like sun mar 18 2012 like that. Please let me know what settings I need to do in code or anything I am missing.

  22. Nathan says

    I was unable to get the app working after the edit function is implemented. I followed the code up to ‘we should be able to see the Note Editor render the selected note’. I was getting the error ‘Uncaught TypeError: Cannot call method ‘setRecord’ of undefined Notes.js:37′ when I click on the disclose button.

    I had to change the the following code in the Notes controller from:

    config: {
    refs: {
    notesListContainer: ‘noteslistcontainer’,
    noteEditor: ‘noteeditor’
    },
    to:

    config: {
    refs: {
    notesListContainer: ‘noteslistcontainer’,
    noteEditor: {
    selector: ‘noteeditor’,
    xtype: ‘noteeditor’,
    autoCreate: true
    }

    },

  23. Tim says

    Jorge –

    Many thanks for this excellent tutorial; it’s been extremely helpful in getting me up and running with my first Sencha Touch app. One question about the view transitions described in this section of the tutorial: how do you get animateActiveItem to work? When I try to run the code exactly as you’ve shown, the console gives me a message saying “Object [object Object] has no method ‘setLayout'”. If I use setActiveItem instead, the views display correctly (although without the slide animation, of course). The Sencha API documentation seems to indicate that animateActiveItem only works with a Card layout, so perhaps it makes sense that it’s not working here (given that we’re moving back and forth from a plain old Container to a form Panel), but I think I must be missing something…

    Thanks,
    Tim

    • says

      Tim, I’m not sure that would be the issue. By default, Sencha Touch apps get a viewport with a card layout, which is what I use in this article.

    • Alexis says

      Hi, Jorge really really thanks, i think that this tutorial can save my life with my final university project, thanks for your work and sharing your skills, however, i have this question: i received the same error that Tim “Object [object Object] has no method ‘setLayout’” don’ t have idea yet? Thanks a lot again!! :)

      • Alexis says

        Sorry i see what is the problem, but i download the .zip for see what we must need add this line: slideLeftTransition: { type: ‘slide’, direction: ‘left’ }, before the function activateNoteEditor and all works ^^ Thanks!!!

        • says

          Hi Alexis and Tim,

          Your getting this error “setLayout” because your calling an undefined variable….

          Put

          slideLeftTransition: { type: ‘slide’, direction: ‘left’ },

          inside Config:{} and instead of calling this.slideLeftTransition, you can do this:

          this.getSlideLeftTransition()

  24. Grover says

    One thing I’ve noticed while running the emulator is that the text field for the title clips/crops the visable amount of text while typing. So in my case if the entered text is too long the text field doesn’t scroll, the cusor vanishes and you have no idea what you are typing. It does save, but you just can’t see what you are typing/saving.

    Any ideas on how to address that?

    • Ellen says

      “Remember, this ref automatically creates a getNoteEditor() function in the controller, which we can use to refer to the NoteEditor instance and make it the active View in the application.”
      I mean I’m not very clear how ref automatically creates a getNoteEditor() ? if my view called XXView, the how about the name of such function?

  25. Ilker says

    Strange error I’m getting:

    TypeError: ‘undefined’ is not a function (evaluating ‘this.getRandomInt(0, 100)’) Notes.js:20

    I tried removing the randomInt, and setting noteId to only stringified current time. There’s no error regarding that particular line now. But I get a different error:

    TypeError: ‘undefined’ is not a function (evaluating ‘animation.setLayout(layout)’) Container.js:1233

    Any comments? Hints?

  26. Jayashree says

    Hi Jorge,
    I’m trying to create a dynamic chart wherein the values from the form should be loaded to the Store.
    I tried with the following code. But I’m not getting the desired output. Can you let me know where I am going wrong?

    CODE IN THE VIEW (form)
    var formPanel = Ext.create(‘Ext.form.Panel’, {
    fullscreen: true,

    items: [ {
    xtype: ‘fieldset’,
    items: [ {
    xtype: 'textfield',
    name: 'name',
    label: 'Name',
    id: 'name'
    }, {
    xtype: 'emailfield',
    name: 'email',
    label: 'Email',
    id: 'email'
    }, {
    xtype: 'passwordfield',
    name: 'password',
    label: 'Password',
    id: 'password'
    }, {
    xtype: 'button',
    text: 'Submit',
    ui: "action",
    listeners: {
    tap: function() {
    var values = JSON.stringify(formPanel.getValues(), null, 100);
    var room = Ext.getStore('Store1').add(values);
    alert(values);
    }
    }

    },]
    }]
    });

    STORE CODE:

    Ext.define(‘Vip.store.FormStore’, {
    extend: ‘Ext.data.Store’,
    requires: “Ext.data.proxy.LocalStorage”,
    config: {
    storeId: ‘FormStore’,
    model: ‘Vip.model.FormModel’,
    proxy: {
    type: ‘localstorage’,
    id: ‘form-val-store’
    },
    autoLoad: true
    }
    });

  27. Jayashree says

    I even tried the below code. It isn’t working either :(

    /* Form view button contents*/
    xtype: ‘button’,
    text: ‘Submit’,
    ui: “action”,
    listeners: {
    tap: function() {
    // var values = JSON.stringify(formPanel.getValues(), null, 100);
    // var room = Ext.getStore(‘FormStore’).add(values);
    // alert(values);
    var FormEditor = this.formPanel();
    var currentNote = FormEditor.getRecord();
    var newValues = FormEditor.getValues();
    // Update the current note’s fields with form values.
    currentNote.set(“name”, newValues.name);
    currentNote.set(“email”, newValues.email);
    currentNote.set(“password”, newValues.password);

    var FStore = Ext.getStore(“FormStore”);
    if (null == FStore.findRecord(‘id’, currentNote.data.id)) {
    FormStore.add(currentNote);
    }
    FormStore.sync();

    }
    }

  28. Jon says

    Hi Jorge,
    Bought the book and love the examples as they are simple to follow and follow a logical path. Just makes the learning curve a little less steep than it would be going through all the API docs! I’m having a problem with my own app though. Main 3 main issues are:

    1) Date Of Birth field, whilst on the model and store look ok when I do a set() from the form’s date picker, get sent to the server on the sync() call as a long number. Not sure why it isn’t sending it correctly. On the server side it doesn’t know what to do with it.

    2) I’d like to be able to revert out any changes if I’ve edited my form and then cancel out of it

    3) Is there a better way of doing set() calls for every field e.g. mapping method which copies across each field?

    If you could show an example with the 3 above issues covered that would be great!

    • says

      Thank you for buying the book!

      – The integer sent to the server as the date is the milliseconds since midnight of January 1, 1970, according to the definition of the Date object.
      – You should be able to use the model’s reject method to revert cahnges.
      – You can use a form Panel’s setRecord and getRecord.

  29. Meex says

    Hi Jorge,

    thx for this great tutorial!
    I’ve a question concerning the toolbar title in the editors view. Is it possible to set this title dynamically at runtime? For example when I want the note’s title in the toolbar?

    regards
    Meex

  30. ankit nanda says

    while tap the new button i am getting the exception
    Uncaught TypeError: Object [object Object] has no method ‘setLayout’
    this error is just occurring at the line
    Ext.Viewport.animateActiveItem(familyEditor, this.slideLeftTransition);
    can someone help me out ASAP.

  31. Carlos Torres says

    Hi Jorge,

    Thank you very much for this tutorial it has been helpful.

    I’m designing my own sencha touch app following this tutorial, but i have a litle issue.

    When i tap the button (activateEditor) it shows the next view, and when i tap the back button of the current view that it send me to the preview it work, but if i tap the button (activateEditor) again it doesnt work.

    Any ideas how to fix this?

    Thanks in advance.

  32. says

    These tutorials have been great so far.

    The one thing that hung me up was how does an existing object in local storage get updated.
    My guess is you’re working with a reference of the original object. Once you update the currentNote object it’s updated in the store.

  33. Meenakshi says

    Hi,

    Do you have tutorial for Sencha Touch2- charts. I have created the charts and it is running fine in the browser but it is not working in the emulator.

    Can you please help.

  34. Naceur says

    Thanks for tuto

    I met the following problem, can you help me plz?
    in the controller notes :
    var currentNote = noteEditor.getRecord(); //return null
    why??
    thanks in advance

    • Thomas says

      go to NotesListContainer and switch “index” and “record” to the following:

      onNotesListDisclose: function (list, index, target, record, evt, options)

      The problem relies on the params order presented in the documentation.

  35. Radha says

    Thank you for the wonderful tutorial.
    After launching the app if I edit the note and close the app and reopen app the previous changes are not getting saved.
    Where am i doing wrong? Please can you fix this problem

  36. Nai says

    Hi,
    Thank you very much for the good tutorial.I would like to see the tutorial about creating Sencha Touch application integrate with RESTFul web service that can work both of online and offline in second edition of your book.

    Thank you so much

  37. shenbaga pandiyan says

    activateNotesList: function () {
    Ext.Viewport.animateActiveItem(this.getNotesListContainer(), this.slideRightTransition);
    }

    In the above code what is the mean for getNotesListContainer(). If its the built in function what it wil do?
    or it is defined somewhere in the code

    • says

      It’s a function created automatically for you when you add the ref to the controller:

      refs: {
      notesListContainer: “noteslistcontainer”,
      noteEditor: “noteeditor”
      }

      If you add a notesListContainer ref and a noteEditor ref, there will be a getNotesListContainer and a getNoteEditor functions.

  38. Silvana says

    Hi!

    This is my first app in Sencha and thanks to this tutorial I’m getting it very easily ;) Now I have the base to write several things I have in mind.

    Thanks for sharing so awesome stuff!

  39. says

    Thanks Jorge for this great tutorial. I follow steps one by one and I learn a lot.

    As mentionned in the comments, declaring “requires:”Ext.MessageBox”,” is needed in the controller to display error message box on Save action. It will be interesting to insert this thing directly in your article for the following readers.

    Have a good day,

  40. Nikhil says

    I am not sure how the values are updated. I would appreciate if anyone can explain as to what is happening behind the scene.

    The only place where we are setting the relationship of Store and the record is when we add a new record. (notesStore.add(currentNote);

    Other than that we are not refreshing the exising values of the localstore from the updated record value. Still the localstore gets updated from the record fields. Mystery!!!

  41. Thomas says

    Hi, first of all thanks a lot for the tutorial! ;)

    Could it be that the params list of “onNotesListDisclose” in NotesListContainer is in the wrong order?

    If i want to edit a note the function passes the index of the tapped entry instead of the record. So i switched “index” and “record” to the following:

    onNotesListDisclose: function (list, index, target, record, evt, options)

    and it works.

    Very odd because the documentation shows the same order you presented.

    Thanks again for the tut! It was very useful to dive into the framework.

Leave a Comment

Your email address will not be published. Required fields are marked *