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.

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="\&quot;notes-list-empty-text\&quot;">No notes found.</div>
<pre>
",
        onItemDisclosure: true,
        grouped: true,
        itemTpl: "</pre>
<div class="\&quot;list-item-title\&quot;">{title}</div>
<div class="\&quot;list-item-narrative\&quot;">{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.

In the next chapter of this series we are going to create a version of the app where the Notes List and Notes Editor Views are defined using config objects, instead of initialize functions.

Stay tuned! :-)

Downloads

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

The Entire Series

Want To Learn More?

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

About Jorge

Jorge is the author of 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.
If you'd like to work with Jorge, contact him at ramonj[AT]miamicoder.com.

Comments

  1. Aishwarya Sharma says:

    Hi Jorge,

    This has been an exceptional help in learning Sencha Touch MVC applications.
    Hope you keep posting many more topics in Sencha Touch, specially Custom Styling using SASS and COMPASS…

  2. This tutorial did help a lot
    What I am struggling with in st2 is how to communicate with sever side data.
    Could you use this example to store notes on sever side (Jason file or database) instead of this localstorages? Thanks

  3. I changed the store’s proxy as following: (uncommend your type&id), but it can’t save physically.
    proxy: {
    //type: ‘localstorage’,
    //id: ‘notes-app-store’
    type: ‘ajax’,
    url: ‘data/notes.json’,
    reader: {
    type: ‘json’
    }
    },

    • The store is not able to save directly to the file. Remember, the store is on the client side, and your json file is on the server side. You will need to create a server-side handler that (1) sends the contents of the json file to the client, and (2) receives data sent by the store and updates the json file.

      • Hi Jorge, your explanation gets rid of my confusions. I need to create server scripts like php to do this.
        it’s nice of you. I hope your future posts cover server-side topics as there are quite few references out there in ST documentations

      • Jorge,
        Is there any way to update that notes data via proxy ‘jsonp’ (cross domain)?
        I can fact that data from data base via php file, which generate the json file, but I have a problem with updating the changed note. I mean the note is updated in the store, but I’d like to be able to update the data in database.

  4. Thanks again Jorge, huge huge help.

    How can I reference the fields in the editor view?

    I’m using Ext.ComponentQuery.query(‘togglefield’)[].setValue(0); which works but it would be better to get by name.

  5. Aishwarya Sharma says:

    Hi Jorge,

    When we are on the NotesListContainer page, if I pull the NotesList with my mouse in the downwards direction there comes a void in between the toolbar and the NotesList. Can this be avoided??

    Can we change the look and feel of the tabpanels etc according to our needs, I mean is it possible at least??

    Thanks and Regards

  6. Aishwarya Sharma says:

    Hi,

    I wanted to ask if it is possible to make an accordion in Sencha Touch 2 .
    I yes then could you just hint how to proceed with it.

    Thanks & Regards

  7. Hi Jorge,

    How setting different handlers for the “disclosure” and “itemtap” events on a list ?

    var servidoresList = {
    xtype: “servidoreslist”,
    store: Ext.getStore(“Servidores”),
    listeners: {
    disclose: {
    fn: this.onServidoresListDisclose,
    scope: this
    },
    itemtap: {
    fn: this.onItemTap,
    scope: this
    }
    };

    Thanks and Regards

  8. Hi,
    First of all thank you for your great tutorial. I have faced a wired issue.
    When I add a few notes, then I remove them and refresh the browser. I get the following error. I would appreciate it, if you could help me on the issue.

    ‘undefined’ is not an object (evaluating ‘record.isModel’)

    Thanks
    Reza

  9. Hi Jorge,

    In your example i changed the title toolbar in file NoteEditor.js as follows
    var topToolbar = {
    xtype: ‘toolbar’
    docked: “top”,
    title: this.record.get (‘title’),
    items: [
    backButton
    ]
    };
    and I get the following error:
    Uncaught TypeError: Can not call method ‘get’ of undefined

    can you help me?

    Thanks and Regards

  10. Aishwarya Sharma says:

    Hey Jorge,

    I have made a view with a toolbar(which has a button firing a tap event) and a panel.
    When I try to set the height of the panel in the controller through the following code:

    onToggleList: function()
    {
    console.log(“In controller”);
    this.getAccordionPanel().getComponent(‘p’).setHeight(’0′);

    }

    it gives me an error “Uncaught TypeError: Cannot call method ‘setHeight’ of undefined”
    where p is the id of the panel.
    Can you tell how I can access the height when getComponent() is not working??

    Thanks $ Regards

  11. Is there any way to upload updated notes to database on the server from packaged app?

  12. Himanshu Rathore says:

    Hey Jorge,
    Brilliant tutorial sir helped me lot.Right now i am wondering how can we integrate sms gateway to my sencha touch 2.0 app.I have gone through the following links and dont find it helpful because the link provided phonegap’s SMS class link but now its deprecated.

    http://www.sencha.com/forum/showthread.php?131445-It-is-possible-to-send-SMS-Messages-from-a-Sencha-Touch-App

    http://stackoverflow.com/questions/3193997/send-sms-through-phonegap-on-android

    So can you show us by a tutorial or code snippet to achieve this.

  13. Unyime Daniel says:

    This is by far the most documented tutorial I have come across the internet.
    I need a book that teacher sencha designer or architect or sencha 2 itself.
    George please start writing one for us. If you know any good one, pls recommend.
    We dont want to start building and fine out that another version of sencha3 is out.

  14. Firstly many thanks for this excellent tutorial. Once I delete a note I can still add new notes and edit them afterwards, but if I refresh the browser it hangs on loading and I get the following error:

    Store.js:904TypeError: ‘undefined’ is not an object (evaluating ‘record.isModel’)

    Looks like removing a record breaks to the store? This happens in both Safari and Chrome. Any help would be appreciated.

    Also, for further articles, it would be great to see how to customise the default themes and work with Compass/Sass. I’d also like to see how that’s done using Sencha Architect 2.

    Thanks again.

  15. hi, jorge
    can you answer me a question unrelevant to this notes app issue. How can i do in ST2 to tap a image to fullscreen and tap again to restore it to original size? (just like we operate in iPhone image rol)l

  16. Juan Carlos Morales Mora says:

    Thanks a lot for the tutorial. It was really helpfull and well explained. A very nice start for the Sencha Touch technology.

    Really nice job, thanks a lot!!!!!!

  17. This give me a whole ST’s technoloy. It was really wonderful. Very Very nice job and thanks a lot.

  18. Hi Jorge,

    on click of list view item the disclose is not working i am using sench touch 2
    onNotesListDisclose: function (list, record, target, index, evt, options) {
    console.log(“editNoteCommand”);
    this.fireEvent(‘editNoteCommand’, this, record);
    },

    This function is not at all calling please help with this issue

    Thanks

    • Ram, I cannot reproduce the issue on my side. Please review your code again.

      • Ext.define(“NotesApp.view.NotesListContainer”, {
        extend: “Ext.Container”,
        alias: “widget.noteslistcontainer”,

        initialize: function () {

        this.callParent(arguments);

        var newButton = {
        xtype: “button”,
        text: ‘New’,
        ui: ‘action’,
        handler: this.onNewButtonTap,
        scope: this
        };

        var topToolbar = {
        xtype: “toolbar”,
        title: ‘My Notes’,
        docked: “top”,
        items: [
        { xtype: 'spacer' },
        newButton
        ]
        };

        var notesList = {
        xtype: “noteslist”,
        store: Ext.getStore(“Notes”),
        listeners: {
        disclose: { fn: this.onNotesListDisclose, scope: this }
        }
        };

        this.add([topToolbar, notesList]);
        },
        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);
        },

        config: {
        layout: {
        type: ‘fit’
        }
        }
        });

        here is the NotesListContainer am i missing something on click of list view the disclose is not calling the function onNotesListDisclose

  19. Thank …

    question…..
    delete in Ext.Msg.confirm use how ???

  20. And launches the delete confirmation window, select yes, you want to make the deletion.

    onDeleteNoteCommand: function() {
    console.debug(“onDeleteNoteCommand”);

    Ext.Msg.confirm(“Warning”, “Are you sure Delete?”, function(btn, text) {
    if(btn == ‘yes’) {
    var noteEditor = this.getNoteEditorView(); // this error how??
    var currentNote = noteEditor.getRecord();
    var notesStore = Ext.getStore(“Notes”);

    notesStore.remove(currentNote);
    notesStore.sync();
    this.activateNotesList(); // this error ??
    }
    });
    },

  21. Hi jorge,

    You have written every function inside initialize function which will call the controller js file
    can you please give some example how to call a function of controller written inside
    config
    {

    xtype:’button’,
    text:’test Call me’,
    flex:1,
    listeners: {
    tap: function () {
    console.log(“test callmeTapped”);
    this.fireEvent(“backToHomeCommand”, this);

    }

    }

    },

    This function is not calling the controller js file

  22. Terence Tian says:

    It is really a nice article, did help me a lot. can you make a sample about routes of controller

    thanks a lot.

  23. Michael McClenaghan says:

    I am receiving the same error as a few other people. “Uncaught TypeError: Cannot read property ‘isModel’ of undefined “. This error occurs when at least 2 notes are added, then one is deleted, and the page is refreshed. The only way to rectify this is to clear the browser cache, which removes all other notes too. Any idea why this is happening? I assume it is something to do with an entry not being deleted in the local-storage.

    • Michael McClenaghan says:

      After looking into it, it seems that the store.remove(record) method is removing the model from the localstore, but is not removing the Id from the proxy itself, so when the Store is loaded on the page refresh, it is trying to lookup an Id that does not exist.

  24. Hi.,
    i wish to connecting mysql database using sencha touch tutorial in android native application..please explain step by step example..because am a beginer for android and sencha-touch.

  25. Gangadhar says:

    Hi, Great thanks to your tutorial…. I have built my application based on your Part5….

    But, my problem is i want to load two views . First view contains list of values coming from url dynamically.

    second view contains the details of the particular item clicked which loads it from other url.
    Now the first list is coming perfectly but, second view is not getting dynamic data of particular item.
    I have declared a global variable , and i am reassigning the global variable value in first container with value of a whatever the clicked item. now the global variable contains the clicked item value. when i am giving this global variable in second url value this not displaying the clicked item value rather it is displaying the starting static global variable value. now how to pass the reassigned global variable value to second url…..

    my url pattern is…
    first url: http://localhost/……collection?&format=xml
    second url: http://localhost/……collection(value=’123′)?&format=xml

    Any help will be highly appreciated….

    • Thanks. Post a link to your code so we can take a look.

      • Gangadhar says:

        Hi Jorge,

        Thanks for your reply…

        As per my understanding ,I think that there is one problem with this type of approach for dynamic data .

        Since all the containers are loading initially in app.js, the dynamic URLs in next containers are not taking the clicked item value of the first list for displaying the details.

        Can we do something so that Containers are loaded one after other and not initially…

        Kindly reply me with suggestive answers… Thanks in advance…

        • Yes. Don’t load them initially. Add them to the viewport as they are needed.

          • Gangadhar says:

            Hi Sir,

            My store is containing URL.
            According to my requirement i want to reload the Store at certain place.
            Is there any way to reload the Store so that i can load dynamic value to the store in Controller file.

            Reply me soon sir…its urgent.Thanks in advance…

            Regards,
            Gangadhar

  26. Gangadhar says:

    Hi Sir,

    Anyway I tried in my own way and the problem got solved.

    the other problem is i want to load Google map into application .
    The map should load according to store’s location.
    As there are different locations, the map should load dynamically when i click on Map button from Employee details container.

    Can you please help me with some code how to load Map when i click on Map button. The Control goes to Controller when i click on Map button. So, the actual logic will be in Controller file.

    Thanks in advance…..

  27. Jorge man am so greatfull for all your posts they really helped me alot….But i wish you can just help with using the same app but using a mysql database….i’ve done all your examples and they are are the best sencha touch 2 examples google can find..please help me with the mysql part..thank you

    • Hang in there Patrick. I will be writing about connecting the application to a server soon. Thank you for being a loyal reader.

  28. Mr. Jorge, let me thank you for all your work you don’t know the important that have this tutorial for me know. Thanks for your time and sharing your skills! ^^

  29. Hi, Mr. Jorge, Well this type I write you because I have a real big problem, When I build the app seems like the models and stores not exist, on the list of notes I can’t see anything, and into New Note view any field show, but this happens just after build. Please can help me?

    I don’t wan’t bother you, but please I need to know what I suppose to do.

  30. Hi,

    Please teach me how to set up the Sencha Touch Framework in detailed tutorial thanks :)

  31. Hi Jorge
    The tutorial is good and everything is working fine,but when i type in narrative field and try to save it a alert msg should fire but its showing the following error,please tell me how to solve this
    Uncaught Error: [ERROR][NotesApp.controller.Notes#onSaveNoteCommand] Using Ext.Msg without requiring Ext.MessageBox

    Regards,
    Girish

    • Add the requires to the NoteEditor. It should en up looking like this:

      requires: ['Ext.Toolbar', 'Ext.form.FieldSet', 'Ext.form.Text', 'Ext.field.TextArea', 'Ext.MessageBox'],

  32. Hi Jorge,

    Thanks jorge now its working fine..Please share if u have some more tutorials..once again thanks for this tutorial.

    Regards,
    Girish

  33. And launches the delete confirmation window, select yes, you want to make the deletion.

    onDeleteNoteCommand: function() {
    console.debug(“onDeleteNoteCommand”);

    Ext.Msg.confirm(“Warning”, “Are you sure Delete?”, function(btn, text) {
    if(btn == ‘yes’) {
    var noteEditor = this.getNoteEditorView(); // this error how??
    var currentNote = noteEditor.getRecord();
    var notesStore = Ext.getStore(“Notes”);

    notesStore.remove(currentNote);
    notesStore.sync();
    this.activateNotesList(); // this error ??
    }
    });
    },

    • Remember that you are inside the confirm’s callback function and “this” no longer holds a reference to the controller.

      • “Code” for me please.

        • onDeleteNoteCommand: function() {
          console.debug(“onDeleteNoteCommand”);

          var me = this;

          Ext.Msg.confirm(“Warning”, “Are you sure Delete?”, function(btn, text) {
          if(btn == ‘yes’) {
          var noteEditor = me.getNoteEditorView();
          var currentNote = noteEditor.getRecord();
          var notesStore = Ext.getStore(“Notes”);

          notesStore.remove(currentNote);
          notesStore.sync();
          me.activateNotesList();
          }
          });
          },

          • OMG !!! Thank you so much Jorge.
            Next issue, I want create Log-in page and check name,password
            Code me

            Ext.application({
            name: “NotesApp”,

            models: ["Note"],
            stores: ["Notes"],
            launch: function() {
            Ext.create(‘Ext.form.Panel’, {
            fullscreen: true,
            items: [{
            xtype: 'fieldset',
            title: 'Register',
            items: [{
            xtype: 'textfield',
            label: 'Name',
            name: 'name',
            type: 'string'
            }, {
            xtype: 'passwordfield',
            label: 'Password',
            name: 'password'
            },
            {
            xtype: 'button',
            text: 'OK',
            ui: 'confirm',
            handler: function() {
            if(name == 'name'){

            Ext.Msg.alert('', 'Login Successful!');}
            else {Ext.Msg.alert('Error', 'Both username and password are required.');}

            }
            }]
            }]
            });
            }
            });

            Help me again!! Please

  34. pls how do i create a PHP for submission of a form in sencha touch 2….please i nid it

  35. Good job on the article, the grouper piece was a huge help

  36. Hello there, thak’s a lot for this tutorial, it did clarify the sencha touch framework to me.

    I have one question which is : can i use sencha touch effects such as animateActiveItem in other framework like bootstrap twitter or zurb foundation?

    Thank’s a lot, knowing this is hugely crucial to me :)
    Best regards

  37. Trung Nguyen says:

    Hello Jorge,

    I have a problem after build app native with this example. From browser I can save the record, close browser and then rerun browser, record still there. But with Android app after using command line. sencha app build native. The app running inside my device. I can save record, but after I stop the app, rerun the app again record has been gone.

    Do you have any ideas?

    Thanks a lot,
    Trung.

  38. Trung Nguyen says:

    Hi Jorge,

    I works fine after I drop folders into an Android www and build the application with Eclipse, not use command line from sencha. Is it bug from sencha?

    Thanks,
    Trung.

  39. Harshita Singh says:

    Hi,

    I have a requirement wherein i want to generate controls dynamically reading the values from json.My json would have values for UI components.Please help me out with this if you have some demos tutorials for same.

    Thanks

  40. Welder Jobs says:

    Howdy I am so excited I found your webpage, I really found you by accident, while I was researching
    on Yahoo for something else, Anyways I am here now and would just like to say thanks a lot for a remarkable post and
    a all round enjoyable blog (I also love the theme/design), I don’t have time to read through it all at the moment but I have saved it and also added in your RSS feeds, so when I have time I will be back to read much more, Please do keep up the excellent work.

  41. Hi Jorge,

    Thank you very much for this nice tutorials series :)
    I learned more with these than I did with the Sencha doc (which is a bit confusing and poor to my sense..).

    I have a question about “sorters”, how to dynamically change the sorter property (currently “dateCreated”) ? With a “filters/criteria” box for example ?

    Thank you :)

  42. Hi Jorge,

    I admire and appreciate your effort in creating such a beautifully explained tutorial. Thanks a lot for the effort. Now could not resist asking one question here on store and grouper config.

    I think grouping the notes is view related stuff. So the grouper config in “view” (here NoteList) should be sufficient. Why there is a “grouper” config for “store” (here Notes) and a “grouped” config in view?

    Thanks!

Speak Your Mind

*