Writing a Sencha Touch Application, Part 4

This is the last of a four-part series on how to write a Sencha Touch application. If you’re new to the series, here are the links to the previous installments:

In part 3 of this tutorial we worked on the Note Editor and added the ability to create notes. It is time now to let our users edit and delete notes. Let’s work on the edit note feature first.

Disclosure events in a Sencha Touch List

When the person using the application touches a note’s disclosure button in the Notes List view, the selected note should be displayed in the Edit Note view:

We can complete this feature by implementing the onItemDisclosure handler of the notes list. Here’s the code:

NotesApp.views.notesList = new Ext.List({
    id: 'notesList',
    store: 'NotesStore',
    onItemDisclosure: function (record) {
        var selectedNote = record;
        NotesApp.views.noteEditor.load(selectedNote);
        NotesApp.views.viewport.setActiveItem('noteEditor', { type: 'slide', direction: 'left' });
    },
    itemTpl: '
<div class="list-item-title">{title}</div>' +
        '<div class="list-item-narrative">{narrative}</div>',
    listeners: {
        'render': function (thisComponent) {
            thisComponent.getStore().load();
        }
    }
});

The handler function accepts the selected note as a parameter. What we need to do in the handler is load the note in the editor utilizing the editor’s load() method, and then, make the Note Editor view active via a call to the viewport’s setActiveItem().

Very cool. At this point we can switch over to the emulator, where we should be able to edit notes.

Removing records from a data store

Deleting notes is also very easy. The Trash button on the Note Editor view’s bottom toolbar will trigger this function. We need to change the button’ handler like so:

NotesApp.views.noteEditorBottomToolbar = new Ext.Toolbar({
    dock: 'bottom',
    items: [
        { xtype: 'spacer' },
        {
            iconCls: 'trash',
            iconMask: true,
            handler: function () {

                var currentNote = NotesApp.views.noteEditor.getRecord();
                var notesList = NotesApp.views.notesList;
                var notesStore = notesList.getStore();

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

                notesStore.sync();

                notesList.refresh();
                NotesApp.views.viewport.setActiveItem('notesListContainer', { type: 'slide', direction: 'right' });
            }
        }
    ]
});

After acquiring references to the current note, the notes list and the notes data store, we use the store’s findRecord() function to find and remove the note loaded on the editor.

Next, we call sync() on the store to make the removal permanent, and proceed to re-render the notes list and make the Notes List view active.

This procedure is similar to the one we followed when we implemented the Save Note feature, although in this case we’re deleting the note, not saving it.

A quick check on the emulator should confirm that we can now delete notes.

Grouping items in a Sencha Touch List

One last thing we want to do is, in the Notes List view, render the notes grouped by date. This will make it easier for our users to work with their notes.

First, we need to tell our notesList Ext.List that it needs to render its items grouped. We can do this using the grouped config option like so:

NotesApp.views.notesList = new Ext.List({
    id: 'notesList',
    store: 'NotesStore',
    grouped: true,
    emptyText: '<div style="margin: 5px;">No notes cached.</div>',
    onItemDisclosure: function (record) {
        var selectedNote = record;
        NotesApp.views.noteEditor.load(selectedNote);
        NotesApp.views.viewport.setActiveItem('noteEditor', { type: 'slide', direction: 'left' });
    },
    itemTpl: '<div class="list-item-title">{title}</div>' +
        '<div class="list-item-narrative">{narrative}</div>',
    listeners: {
        'render': function (thisComponent) {
            thisComponent.getStore().load();
        }
    }
});

Then, we need to override the getGroupString() function of the NotesStore:

Ext.regStore('NotesStore', {
    model: 'Note',
    sorters: [{
        property: 'date',
        direction: 'DESC'
    }],
    proxy: {
        type: 'localstorage',
        id: 'notes-app-store'
    },
    getGroupString: function (record) {
        if (record && record.data.date) {
            return record.get('date').toDateString();
        } else {
            return '';
        }
    }
});

You can define the grouping behavior of the store using the groupField property and the getGoupString() function.

The getGroupString() function returns the string to group on, based on the store’s data model’s properties. By default, getGroupString() returns the value of the groupField property. In our case we want to use the value of the note’s date as the group’s header, but we want to format the value as a date.

If we check the emulator, the notes taken the same day should render under the same group header:

We made it!

This is pretty much it. A very simple Sencha Touch application and an easy way to learn some of the features of the Sencha Touch framework.

I hope you take this app to the next level as you learn more about the framework. :-)

And don’t forget to leave a comment letting me know the Sencha Touch topics about which you would like me to write.

Download the Notes App: Notes-App-v1.0.zip

Here’s the rest of the series:

About Jorge

Jorge is the author of Building a Sencha Touch Application and the Ext JS 3.0 Cookbook, and technical reviewer of Learning Ext JS and Pro jQuery Mobile. 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. Dave Fowler says:

    Great tutorial

    I found it really useful and informative – would love to see a tutorial on turning it into a true phone app with integrated database :)

  2. J-Hong, AHN says:

    Thanks. Very Helpful.
    Please keep go on.

  3. Ricardo Amorim says:

    Great tutorial!

  4. These tutorials have been very knowledgeable! Hope this isn’t the end of the Sencha Tutorial Series. I think you are actually on to something here.

  5. Jorge says:

    Thanks everybody! Please let me know what other topics you would like me to write about.

    • coolon says:

      I think it would be really useful to make some topics about another simple sencha touch application – RSS reader

      Also its interesting to read about sencha touch customization – custom buttons and custom app design

      thanks

    • Ricardo Amorim says:

      Localstore with associations! it would be great.

  6. Great series of articles. Would love to see more like this.

  7. Dario J. says:

    This is an excellent tutorial! However, I wonder how would you modify it to use remote store (ajax or rest proxy) instead of local store? I tried to modify proxy property of the store:

    proxy : {
    type : ‘ajax’,
    url : ‘/notes.php’,
    reader : { type : ‘json’ }
    }, (…)

    where “notes.php” is simple script on the that returns JSON encoded list of notes from the database.

    Now, list of notes gets populated, as well as edit note form. But when you try to save changes (Save button), or add new note, nothing happens. Notes app sends empty POST request to server (notes.php script)?

    Any suggestions? Thanks!

  8. Stenlo says:

    Thanks, this is a very nice tutorial. How would you save the notes on a server and not only locally?

    Best,
    Stenlo

  9. Jorge says:

    Looks like I will need to write about saving to a server. :-)

  10. Klaus says:

    Thanks for this great tutorial. My suggestion:

    Part 5, which shows how to break the whole Script in separate parts (like mainapp, list, editor…) and how to give it a good structure in filesystem.

    (reference: http://www.sencha.com/learn/Tutorial:Writing_a_Big_Application_in_Ext)

  11. Jim says:

    Very well done. Complete with Create, Read, Update and Delete operations on data store. I learned a lot. Thanks.

  12. TJ says:

    Well done.

    I agree with Klaus that breaking it up into the Sencha recommended directories would be a nice next step.

    It would also be great if you could add pointers to more general discussions of the various topic areas you introduce: e.g., models/stores/dataviews; navigation; verification and validation, etc.

    But, overall, this is an excellent tutorial as it is.

    Thanks!

  13. Jorge says:

    You’re welcome, guys.

  14. Ted says:

    Great tutorial. I’ve got a question. In the section called “Disclosure events in a Sencha Touch List”, in the code on about line 12, you define a listener for the ‘render’ event. What is this for? Thanks in advance.

  15. Oleg says:

    Awesome tutorial, well done!

    I would love to see some tutorials on TDD and BDD using Jasmine and how to run the tests on a build server.

    Thanks and keep blogging

  16. Homan says:

    Hey!
    Great tutorial!

    I have one question though:

    I’m making an application where I want to populate a textfield with the record from a list item and I just can’t get it to work. Could you point me in the right direction?

    Again, great tutorial…it helped me a bunch!

    Best regards,
    Homan

  17. Brad says:

    Outstanding article! Thank you. In addition to the remote JSON via AJAX questions already mentioned above, can you please explain what the listeners: ‘render’ is for in the NotesList? This just showed up between parts 3 and 4 in the code without saying why.

  18. Dinesh says:

    Thanks for the wonderful tutorial. I have a question. when I use this line of code NotesList.refresh(); in this blockNotesApp.views.noteEditorBottomToolbar = new Ext.Toolbar. I am not able to go back to Note list when I delete a note. When I did not use refresh it work fine. Can you tell me what could be the problem?

  19. Michael says:

    Thanx Jorge. Your tutorial was very helpful.

  20. Scott B says:

    It would be great to see a tutorial on adding a logo to the top of the app and changing the color schemes.

  21. Zach M says:

    How is the localstorage suppose to work? When I refresh in my iPhone browser the data gets wiped. Anyone else having this issue? Thanks in advance.

  22. xmo says:

    Hey! Really helpful tutorial for begginers like me! I have one question, how can I change the code to pass the informations in the note’s list to another panel and show this information again in a html format instead to load the informations in a form? I want it to show more detailed informations about each item in my list. Thank you!

  23. AOXO says:

    I am also experiencing problems with the local store when I refresh my browser or restart my Ipad the notes are wiped.
    Another minor issue is when my note is delete via the delete button int he bottom toolbar the notes list view does not become active. Would appreciate your suggested solution and how to save notes to a server

  24. CM says:

    Hey, thx for this great tutorial, thx to you i was able to make the list fit, i was trying that for while now… .

  25. Muhammad Faisal says:

    Thnx jorge, this article was very helpfull.
    i am facing a problem. when i generat grouped list according to ur method then a vertcal bar at right side will be displayed having (A – Z) alphabets. i want to remove this bar plz help me.
    something like that;
    A
    B
    C
    D
    E
    F….

  26. Thanks a lot Jorge. This tutorial series was very helpful. :)

  27. Uche Ngadi says:

    This tutorial really helped me to get started with Sencha Touch. However, i will request that you provide us with a tutorial on retrieving and manipulating data from a database say MySQL.

    Thank you very much all the same.

    Good work.

  28. Kiu Felix says:

    Great articles. I can now do a meaningful coding. Great tutorial on Sencha.

  29. Ivan Moncada says:

    Really really a great tutorial for beginers. Very clear, methodic, and helpful. Thanks!!!, it saved my weekend.

  30. SUKANTA GHORU says:

    Hi Jorge,

    Thanks you very much to write a wonderful article with Sencha Touch for beginners.

    This tutorial really helped me to get started with Sencha Touch. It would be really helpful for a beginner like me if you please share a tutorial on retrieving and manipulating data from a database like SQL Server. User can work with records in offline and when Internet connection will get available, then all the records stored locally will be sent to the DataBase.

    Great work.

    Thanks & Regards,
    Sukanta Ghorui

  31. Anıl Gür says:

    Hİ Jorge ,

    Thanks tutorial is very useful especially toolbar spacer object. I couldnt find anywhere use spacer object via this tutorial Iearned how can I use it.

    Regards.

  32. kprk says:

    Hi Jorge,
    It is really awesome. This is what I was looking for . I was goggling since morning to know how to start ST application, but you saved my day. I am already reader of your cookbook. Can you update some articles on ST2 as it is released couple of days before.

  33. Andy Jones says:

    Jorge
    Many thanks as a beginner here your Notes example provides a simple way of understanding the components of the app and how they work together.
    I do have an ask however, do you have an example with how i can access information from a website and display within my app. for example hooking into my tweets or being able to render news items from a website.
    I am also looking to build my own website with a database from which i provide the data for my mobile pages. i take it i need to do this using something like php. do you have any simple references to how to achieve this.

    Many thanks again.
    Andy j

  34. AAT says:

    I echo the comments of gratitude. Marvelous job especially in contrast with the “Getting started” guides on Sencha website (their 2.0 guides make no sense whatsoever i.e. following the code examples result in error after erro) Really bad for someone just getting into the framework.

    Your work is truly laudable. Thank you for taking the time!

    I look forward to reading your tutorials on working with JSON and served data.

  35. Andreas says:

    Thank you for this very helpfull tutorial.
    Have you any tutorial like this or ported it to use Sencha Touch 2?

  36. Aidan says:

    The tutorial was really helpful.

    There’s just one problem when i run the application. When i remove a note and refresh my browser the notes app crashes. The error i get is ” Cannot read property ‘id’ of null”. This happens inside the Sencha Touch debug file, not the notes script itself. My guess is that something goes wrong with the local storage.

    Has anyone else had this problem and if so, how did you solve it? I’ve encountered this problem within my own version of the app as have i in the version that Jorge has put at the end of the tutorial.

    Thank you!

  37. Adrian L says:

    Great tutorial man!!

    This gave me a great insight on how to start doing my own sencha touch app, thank you very much! Now let’s expand it even more!! BTW… have u tried to make it a native app with phonegap? Any advice on this? Saludos bro!

    Adrian

  38. DaveQC says:

    Jorge,

    Excellent tutorial! I had a great time following along building the app step by step and I have to say I was pleasantly surprised and very happy that it worked every step. It was my first attempt with building a mobile app. i used Eclipse along with the Android ADT plugin, the PhoneGap plugin by Mobile Developer Solutions and was able to build it as a native app quite effortlessly!

    Thanks again for making it possible.

    Dave

    P.S. If you were to continue with more tutorials on this app, I would love to see how you would integrate it with a server side storage solution such as MongoDB or MySQL and having the application be able to sync while connected.

    • Jorge says:

      Thank you, Dave. It’s very rewarding to see how these tutorials have helped so many people.

      Stay tuned. I will be talking about server-side storage and synchronization in the coming weeks.

  39. max says:

    App works fine but im having problems with making app offilne using cache manifest? It works offline perfectly fine for a few times i use it, then all my notes disappear from my phone when I reconnect to the internet? I think some should touch up on this topic also, that would make a complete package of this great app.

  40. Andres says:

    Excellent tutorial my brother, let me congratulate you for this job.

  41. Juan says:

    Jorge great tutorial.

    I have one question, how to control that the same note is not saved more than once on the local storage? In my localstorage, the record is store one time, but on the id key keeps adding the id even that is already present, if the user press the save button again and again. The consequence of that is when I delete the item from my list, it will still be shown on the list if the id is more than one time.

  42. THE MAD CODER says:

    This was a really great start to finish tutorial. Great job and thank you.

  43. Andres says:

    I have read that there is a component that ExtJs ComboBox is a variation of this component is called TimeField and serves to display values ​​over time. Most properties are the same as the ComboBox. But I failed to get results like to know if you can help with that.

  44. Varun Mahajan says:

    Very nice tutorial!
    Thanks! hope to see more from you soon!

  45. tony says:

    I’ve typed in the code, I’ve copied and pasted from the web and the book and no luck compiling. Mac and Aptana. Can you up load the source files? I have found differences between the web and book versions. I’m a Flex guy trying to get into to this and would appreciate it.
    Thanks,
    Tony

    • Jorge says:

      Tony, I’m not sure what you mean that you’ve had no luck compiling. Please post the specific error you’re seeing so we can help you.

  46. Hacho says:

    I ran through your tutorial making my notes as I went along. My intent was to get familiar with the API and the calls you were making. The only thing that caught me off guard / confused me was in your last edit of:

    views.noteEditorBottomToolbar

    In it, you reference

    currentNote.data.id

    I didn’t find anywhere in the API that said that indicated Model “data” could be accessed that way. The closest i got was a get() method.

    I also noticed in the very last edit, defining the getGroupString function, you use both methods to reference the “data” in a Model object

    if (record && record.data.date) {
    return record.get(‘date’).toDateString();

    Can you shed a little more light for me?

    By the way, fantastic tutorial. I actually bought the PDF because it was so good.

    • Jorge says:

      Thank you, Hacho. Both ways are acceptable. I personally prefer using accessor methods (get(‘something’) or getSomething()) if they exist. But I wanted to show both ways in the tutorial.

  47. boopathi says:

    Very useful tutorial..thank u..

  48. Adams says:

    great tutorial. thanks so much Jorge…

  49. Training was very good
    Please description save the contents of the cache in the database…
    thank you.

  50. Jeffyt says:

    Great tutorial!

    I have worked through it have a working demo application!

    I do have one question that I am struggling with, I would like to add a check box to the note screen. In my code below I can see the checkbox on the note edit screen and check it but it saves it as false always.

    Here is my model:
    Ext.regModel(‘Note’, {
    idProperty: ‘id’,
    fields: [
    { name: 'id', type: 'int'},
    { name: 'date', type: 'date', dateFormat: 'c' },
    { name: 'title', type: 'string'},
    { name: 'complete', type: 'boolean' },
    { name: 'narrative', type: 'string'}
    ],
    validations: [
    { type: 'presence', field: 'id' },
    { type: 'presence', field: 'title', message: 'Please enter a title for this note.' }
    ]
    });

    Here is my noteEditor

    //Create the Sencha Touch Form Panel
    NotesApp.views.noteEditor = new Ext.form.FormPanel({
    id: ‘noteEditor’,
    items: [
    {
    xtype: 'textfield',
    name: 'title',
    label: 'Title',
    required: true
    },
    {
    xtype: 'textareafield',
    name: 'narrative',
    label: 'Narrative'
    },
    {
    xtype: 'checkboxfield',
    name: 'complete',
    label: 'Complete'
    }

    ],
    dockedItems: [
    NotesApp.views.noteEditorTopToolbar,
    NotesApp.views.noteEditorBottomToolbar
    ]
    });

    Here is my noteListToolbar

    //Create the Sencha toolbar
    NotesApp.views.notesListToolbar = new Ext.Toolbar({
    id: ‘notesListToolbar’,
    title: ‘My Notes’,
    layout: ‘hbox’,
    items: [
    { xtype: 'spacer'},
    {
    id: 'newNoteButton',
    text: 'New',
    ui: 'action',
    handler: function () {

    var now = new Date();
    var noteID = now.getTime();
    var note = Ext.ModelMgr.create(
    { id: noteID, date: now, title: '', narrative: '', complete: ''},
    'Note'
    );

    NotesApp.views.noteEditor.load(note);
    NotesApp.views.viewport.setActiveItem('noteEditor', {type: 'slide', direction: 'left'});
    }
    }
    ]
    });

    • Jorge says:

      Jeff, where’s the function that saves the note?

      • Jeffyt says:

        Sorry…left that out…(thanks for the quick response)

        NotesApp.views.noteEditorTopToolbar = new Ext.Toolbar({
        title: ‘Edit Note’,
        items: [
        {
        text: 'Home',
        ui: 'back',
        handler: function () {
        NotesApp.views.viewport.setActiveItem('notesListContainer', { type: 'slide', direction: 'right' });
        }
        },
        { xtype: 'spacer' },
        {
        text: 'Save',
        ui: 'action',
        handler: function () {

        var noteEditor = NotesApp.views.noteEditor;

        var currentNote = noteEditor.getRecord();
        // Update the note with the values in the form fields.
        noteEditor.updateRecord(currentNote);

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

        var notesList = NotesApp.views.notesList;
        var notesStore = notesList.getStore();

        if (notesStore.findRecord(‘id’, currentNote.data.id) === null) {
        notesStore.add(currentNote);
        } else {
        currentNote.setDirty();
        }

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

        notesList.refresh();

        NotesApp.views.viewport.setActiveItem(‘notesListContainer’, { type: ‘slide’, direction: ‘right’ });

        }
        }
        ]
        });

  51. Ian says:

    i just started checking out sencha touch and this tutorial has been a very great jumpstart for me. very smooth & well-explained. kudos!

    “Stay tuned. I will be talking about server-side storage and synchronization in the coming weeks.”

    Please let me know if you’ve made a php-mysql-sencha app tutorial or anything similar. thanks!!

  52. Hi Jorge,
    Thanks for this useful guides. I’ve implemented you sample codes. Unfortunately I have 2 problems:
    1. I cannot save a note that I edit.
    2. I cannot delete a note.

    On Save button’s handler, I put some console.log, like this:

    var notesList = NotesApp.views.notesList;
    var notesStore = notesList.getStore();

    if (notesStore.findRecord(‘id’, currentNote.data.id) === null) {
    notesStore.add(currentNote);
    console.log(‘Notes store add current note’);
    } else {
    currentNote.setDirty();
    console.log(‘Make current note dirty’);
    }

    notesStore.sync();
    console.log(‘Notes store synched’);
    notesStore.sort([{property: 'date', direction: 'DESC'}]);

    notesList.refresh();
    console.log(‘List refreshed’);

    NotesApp.views.viewport.setActiveItem(‘notesListContainer’, {
    type: ‘slide’, direction: ‘right’
    });

    I got every log is passed but the list item is not update after I edit a row from the list.

    The same case happened when I tried to delete a content. Here is my code in trash button:

    var currentNote = NotesApp.views.noteEditor.getRecord();
    var notesList = NotesApp.views.notesList;
    var notesStore = notesList.getStore();

    if (notesStore.findRecord(‘id’, currentNote.data.id)) {
    console.log(‘Deleting current note.’);
    notesStore.remove(currentNote);
    }

    console.log(‘Syncing.’);
    notesStore.sync();

    console.log(‘Refresh list with .’);
    notesList.refresh();

    console.log(‘Move to list.’);
    NotesApp.views.viewport.setActiveItem(‘notesListContainer’, {
    type: ‘slide’, direction: ‘right’
    });

    Every log seem so normal. I don’t think notesStore.sync() works like I expected.

    Please share your experince of what migh be wrong in my code. Thanks in advance.

    • Jorge says:

      Amri, I cannot reproduce the issue you described. what is the Sencha Touch version are you using? Also, can you send me your source code via email?

  53. Sam says:

    For a simulator, Can I use Safari? The code for the first part of the app loads, but none of the subsequent ones do, not even after downloading the final app and running… Should this not working in Safari?

  54. alexis says:

    i have a problem. i can navigate to the new page ( noteEditor ) but if i try to go back it won’t work. it has the same code.

    //This one works
    App.views.mainTopbar = new Ext.Toolbar({
    id: ‘mainTopbar’,
    dock:’top’,
    title: ‘Mobile Todo’,
    layout: ‘hbox’,
    items: [{
    xtype:'spacer'
    },{
    xtype:'button',
    text:'New',
    handler:function(){
    App.views.viewport.setActiveItem('addTodoContainer',{type:'slide',direction:'right'});
    }
    }]
    });//end of mainTopbar

    //This one doesn’t
    App.views.addTopbar = new Ext.Toolbar({
    id: ‘addTopbar’,
    dock:’top’,
    title: ‘Add Todo’,
    layout: ‘hbox’,
    items: [{
    ui:'back',
    text:'Back',
    handler:function(){
    Ext.Msg.alert("INFO","No error here");
    App.views.viewport.setActiveItem('mainTodoContainer',{type:'slide',direction:'left'});

    }
    }]
    });//end of mainTopbar

  55. Roby says:

    When I save one note I noticed that in notesStore I can’t read the last notes id (but something like “ext-record-1″), when I refresh the page I can read the id . . . I don’t know why . . .

    I use this: console.log(notesStore.data.keys);
    the result is (:
    [idNote1, idNote2, ..., "ext-record-n"]
    when I refresh the page with the same command I obtain [idNote1, idNote2, ..., idNoten]

  56. Justin says:

    Huge thanks for putting this tutorial together and for keeping up with the comments.

    I’m working on implementing some geolocation information to each note, which I’ve done successfully upon creating the note. However, I cannot seem to figure out how to update the latitude and longitude fields I’ve setup in my model when the Note is updated.

    I have a similar question about updating the date field upon edits as well.

    I assume these pieces would need to be in the save function, but in trying to use the var.set() method I can’t seem to reach the datastore.

    Any ideas?

    Sorry if that’s awkwardly written, still trying to get a hold of the language, so to speak.

  57. Marian says:

    Hi Jorge,

    since Sencha Touch 2 Beta 3 is ready, and API is frozen, can we expect a ST2 version of your tutorial soon?

    Btw, wonderful job on this tutorial, it helped me a lot.

Speak Your Mind

*