How to Create a Sencha Touch 2 App, Part 5

Many of the readers of this tutorial on how to build a Sencha Touch Application have requested a version of the Notes Application that shows how to create components using the config object, instead of the initialize function. In this chapter of the tutorial, we are going to do just that.

Using Sencha Touch’s Config Object to Create the Notes List

The first step we are going to take is consolidate the Notes List Container View and Notes List View into a single View, which we will call Notes List View. This View has the same components that used to exist in the former Views:

In the application’s view directory, we are going to delete the old NotesListContainer.js file, and leave only the NotesList.js and NoteEditor.js files:

Next, we are going to remove the existing code from the NotesList.js file, and define the NotesList class like so:

Ext.define("NotesApp.view.NotesList", {
    extend: "Ext.Container",
    requires:"Ext.dataview.List",
    alias: "widget.noteslistview",

    config: {
        layout: {
            type: 'fit'
        },
        items: [{
            xtype: "toolbar",
            title: "My Notes",
            docked: "top",
            items: [
                { xtype: 'spacer' },
                {
                    xtype: "button",
                    text: 'New',
                    ui: 'action',
                    itemId: "newButton"
                }
            ]
        }, {
            xtype: "list",
            store: "Notes",
            itemId:"notesList",
            loadingText: "Loading Notes...",
            emptyText: '<div class="notes-list-empty-text">No notes found.</div>',
            onItemDisclosure: true,
            grouped: true,
            itemTpl: '<div class="list-item-title">{title}</div><div class="list-item-narrative">{narrative}</div>'
        }],
        listeners: [{
            delegate: "#newButton",
            event: "tap",
            fn: "onNewButtonTap"
        }, {
            delegate: "#notesList",
            event: "disclose",
            fn: "onNotesListDisclose"
        }]
    },
    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);
    }
});

If you are already familiar with the previous version of this View, you will notice that although the onNewButtonTap and onNotesListDisclose functions remain unchanged, we are now taking advantage og the config object to define the View’s items, and the event listeners needed for the New button and the disclose buttons of the notes List.

Defining Event Listeners with Sencha Touch Config Objects

The New button’s tap listener is pretty straightforward. Its delegate config is the value of the itemId config of the button. The fn config is a pointer to the onNewButtonTap function:

{
    delegate: "#newButton",
    event: "tap",
    fn: "onNewButtonTap"
}

We defined the List’s disclose handler in a similar fashion. The delegate points to the List’s itemId, and fn is the onNotesListDisclose function:

{
    delegate: "#notesList",
    event: "disclose",
    fn: "onNotesListDisclose"
}

Configuring the Note Editor

Now we are going to move on to the NoteEditor View, where we will replace the initialize function with the config’s items and listeners properties:

Ext.define("NotesApp.view.NoteEditor", {
    extend: "Ext.form.Panel",
    requires: "Ext.form.FieldSet",
    alias: "widget.noteeditorview",
    config: {
        scrollable: 'vertical',
        items: [
            {
                xtype: "toolbar",
                docked: "top",
                title: "Edit Note",
                items: [
                    {
                        xtype: "button",
                        ui: "back",
                        text: "Home",
                        itemId: "backButton"
                    },
                    { xtype: "spacer" },
                    {
                        xtype: "button",
                        ui: "action",
                        text: "Save",
                        itemId: "saveButton"
                    }
                ]
            },
            {
                xtype: "toolbar",
                docked: "bottom",
                items: [
                    {
                        xtype: "button",
                        iconCls: "trash",
                        iconMask: true,
                        itemId: "deleteButton"
                    }
                ]
            },
            { xtype: "fieldset",
                items: [
                    {
                        xtype: 'textfield',
                        name: 'title',
                        label: 'Title',
                        required: true
                    },
                    {
                        xtype: 'textareafield',
                        name: 'narrative',
                        label: 'Narrative'
                    }
                ]
            }
        ],
        listeners: [
            {
                delegate: "#backButton",
                event: "tap",
                fn: "onBackButtonTap"
            },
            {
                delegate: "#saveButton",
                event: "tap",
                fn: "onSaveButtonTap"
            },
            {
                delegate: "#deleteButton",
                event: "tap",
                fn: "onDeleteButtonTap"
            }
        ]
    },
    onSaveButtonTap: function () {
        console.log("saveNoteCommand");
        this.fireEvent("saveNoteCommand", this);
    },
    onDeleteButtonTap: function () {
        console.log("deleteNoteCommand");
        this.fireEvent("deleteNoteCommand", this);
    },
    onBackButtonTap: function () {
        console.log("backToHomeCommand");
        this.fireEvent("backToHomeCommand", this);
    }

});

We are following the same approach we used to configure the NotesList View. This time, we need listeners for the Back, Save and Delete buttons:

listeners: [
    {
        delegate: "#backButton",
        event: "tap",
        fn: "onBackButtonTap"
    },
    {
        delegate: "#saveButton",
        event: "tap",
        fn: "onSaveButtonTap"
    },
    {
        delegate: "#deleteButton",
        event: "tap",
        fn: "onDeleteButtonTap"
    }
]

The onBackButtonTap, onSaveButtonTap, and onDeleteButtonTap function remain unchanged.

Adding View Instances to the Application

In the app.js file, we are going to instantiate both Views as follows:

Ext.application({
    name: "NotesApp",

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

    launch: function () {

        var notesListView = {
            xtype: "noteslistview"
        };
        var noteEditorView = {
            xtype: "noteeditorview"
        };

        Ext.Viewport.add([notesListView, noteEditorView]);

    }
});

Modifying the Controller

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

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

    extend: "Ext.app.Controller",
    config: {
        refs: {
            // We're going to lookup our views by alias.
            notesListView: "noteslistview",
            noteEditorView: "noteeditorview",
            notesList: "#notesList"
        },
        control: {
            notesListView: {
                // The commands fired by the notes list container.
                newNoteCommand: "onNewNoteCommand",
                editNoteCommand: "onEditNoteCommand"
            },
            noteEditorView: {
                // The commands fired by the note editor.
                saveNoteCommand: "onSaveNoteCommand",
                deleteNoteCommand: "onDeleteNoteCommand",
                backToHomeCommand: "onBackToHomeCommand"
            }

        }
    },
    // Transitions
    slideLeftTransition: { type: 'slide', direction: 'left' },
    slideRightTransition: { type: 'slide', direction: 'right' },

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

        var noteEditorView = this.getNoteEditorView();
        noteEditorView.setRecord(record); // load() is deprecated.
        Ext.Viewport.animateActiveItem(noteEditorView, this.slideLeftTransition);
    },
    activateNotesList: function () {
        Ext.Viewport.animateActiveItem(this.getNotesListView(), this.slideRightTransition);
    },

    // Commands.
    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);

    },
    onEditNoteCommand: function (list, record) {

        console.log("onEditNoteCommand");

        this.activateNoteEditor(record);
    },
    onSaveNoteCommand: function () {

        console.log("onSaveNoteCommand");

        var noteEditorView = this.getNoteEditorView();

        var currentNote = noteEditorView.getRecord();
        var newValues = noteEditorView.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();
    },
    onDeleteNoteCommand: function () {

        console.log("onDeleteNoteCommand");

        var noteEditorView = this.getNoteEditorView();
        var currentNote = noteEditorView.getRecord();
        var notesStore = Ext.getStore("Notes");

        notesStore.remove(currentNote);
        notesStore.sync();

        this.activateNotesList();
    },
    onBackToHomeCommand: function () {

        console.log("onBackToHomeCommand");
        this.activateNotesList();
    },

    // Base Class functions.
    launch: function () {
        this.callParent(arguments);
        var notesStore = Ext.getStore("Notes");
        notesStore.load();
        console.log("launch");
    },
    init: function () {
        this.callParent(arguments);
        console.log("init");
    }
});

Summary

We just created a version of the Notes Application where, instead of the initialize function, we exclusively used config objects to configure each of the application’s Views. In the process, we learned how to create event listeners for components defined using config objects.

At this point, we have accomplished our goals for this application. I hope the insights you gained in this Sencha Touch tutorial will help you create great mobile applications.

Downloads

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

The Entire Series

Want To Learn More?

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

What Sencha Touch Topics Would You Like Me To Write About?

I’d like to help you learn Sencha Touch better and faster, but I need you to let me know what subjects you are curious about, or what topics you are struggling with.

Please send me your questions or suggestions for future posts at miamicoder[AT]gmail.com.

Comments

  1. Aishwarya Sharma says

    Hi,

    Wonderful post Jorge!!!
    I used to do this method earlier, so finally seeing the code in this way is really good.

    Hey, I had another doubt about Sencha Searchfield…It is by default with rounded corners, and they say that it is because they have used HTML5 search. Is there any way it can be altered to make it rectangular. I have made extensive changes in the sass and css but to no success.. Kindly help if you can..

    Thanks&Regards

    • says

      Thanks! Interesting question. I’ve never tried to change the look of the search field in ST. Let me research the topic.

      • Aishwarya Sharma says

        Hi George,

        Writing to tell you that by doing $include-border-radius: false in the _variables.scss I was able to achieve square corners, but it has turned off all the radius property to 0 in all the components. Luckily I have no rounded components to develop!!! :)..but u find any other way, do tell us whenever you delve for this topic..

        Thanks & Regards

      • andy says

        how can i use this demo.i mean i download NotesApp-ST2-Part5.zip.extract it in my tomacat webapp,but i can not see the result. Is there anything wrong?

  2. Lin says

    hi,
    this way using config object looks straightforward. it is more readable.
    I’m wondering where you get these properties, like event: tap, delegate: button.
    which i research sencha docs and haven’t found.

  3. Iceberg says

    ¡Error!
    Hi, good Example, thanks, i have one question, i run your example app, but when i delete some item list, and reload the page , an error rise in the browser:
    Uncaught TypeError: Cannot read property ‘isModel’ of undefined

    What’s going on?

    Thanks

    • AfroLoGeek says

      Check the syntax or the way you declare/use isModel i had similar issues vith case sensitive (for example) viewpoint.isValid and changed it with Viewpoint.isValid
      HTH

  4. AfroLoGeek says

    Hi Jorge !

    Great Great explanations in this tutorial which is guiding a lot the beginner i’m right now! I also bought your ebook and will follow it again as i’m reviewing the current tutorials.
    I’m running into few issues like my list is not showing up, even if i create a new note and save it;also typos in the view notesList with \&quot syntax

  5. Aishwarya Sharma says

    Hi George,

    I just wanted to ask if u knew how to open a new window in safari on a button click…
    windows.open() is not working, even with a blank attribute..

    Thanks & Regards,

      • Aishwarya Sharma says

        Hi Jorge,

        Actually I need to open a pdf file in my iPad application. The problem with using iFrames is that it renders the pdf open but it is not scrollable. So I had to try to open it on the native browser.
        I am not able to get the window opened through sencha touch commands.

        Sorry for the late post, too much work this side.. :)

  6. Ram says

    Hi jorge,

    Thanks for the Wonderful post i really liked it.

    can you please provide an MVC tutorial on Carousel where images come from Web service

    Thanks
    Ram

  7. sathish says

    Hi Jorge,
    Is it possible to write an article with a small example on using JsonP with Sencha Touch2 . There are lot of people (including me) facing issues using Sencha Touch2 with JsonP.
    Thanks,
    Sathish G.

  8. billyb says

    Jorge,

    By far the best Touch 2 example I have seen. Got a question on using the MVC paradigm.

    I understand using controllers to replace placing events (such as TAP) under each component, but is there a good reason why I should not just call a function from the event handler of the component. Both methods prevent having to write the same code. I kind of like knowing that the button’s TAP event is calling my PostDataToServer() function. Also I would believe that referencing a function defined at the application level would be found faster then having the controller locate the button with the #myButton ItemId. I understand that I can define multiple controllers, but for most apps this seems like a bit of overkill. I want to use MVC, but it has to make sense. Any examples you can provide to show why MVC is the better way to go would be very appreciated..

    • says

      Thank you. I understand what you are saying, and I think that for small apps where the requirements will never change, and where there’s sole developer or a very small team, it might feel like you have to write more code and do not gain much by moving business logic out of the views. However, requirements will always change, in particular in larger apps. Teams are not always of one person. In situations like those, you don’t want your views invoking BL functions directly. I would argue that this is also true when you are a sole developer. You don’t need your views to know how the app’s BL works, because if you change the way the BL works, you will need to change the views. This is bad specially if you have to change the app after months of being in production, when you’ve already forgotten how many views call a given function, for example. There are more benefits to the MVC implementation in ST2, such as auto setters and getters, but I won’t go in detail about them here.

      • billyb says

        Thank you for your reply. I agree with what you said about BL not being in the VIEW. When I code an event for a component in most cases, My event code has only 1 line which is a call to a function I define at the app level. I usually pass THIS and the component (maybe a Record Object if applicable) as params to my function. So I am in a way keeping the business logic out of the view. It just seems to me that converting an event to a controller action is a lot more overhead for the JS engine. Also I like knowing while I am viewing my “views” in code what functions the various events I or others have created are calling. With PURE MVC you lose this visibility and have to search thru your controller functions to see what components and events a function is referenced to. If I am missing the point, I would love an example. I do not want to continue coding in this style if there is a better way, I just don’t see it (yet!). By the way. I am using Sencha Architect 2 (Paid Version). It has a lot going for it, but really miss using PHP storm (Code Completion spoils you).

        • billyb says

          Just finish watching a video with Ed Spencer (Sencacon 2011) about MVC and got a lot of questions answered. BUT !!!! it would seem that unless you have a very complex app with lots of views and events to manage, my original assumption seems better IMHO. The amount of code you need to write and the method that MVC uses to link an event to a specific function (Controller Action) requires more effort (code) than just calling a function directly from a component listener events. Maybe I am old school (Hard to teach old dogs new trick) but for small to medium size apps it seems a bit overkill. Since you have to define an ID: value to use Controller actions, It would appear they (Framework) are doing a getCmp() behind the scenes.

          I do really appreciate the time an effort you have spent on your tutorial and hope you continue to publish additional examples of what can be done with Sencha touch.

          I have high hopes that Sencha Architect will at some point allow the addition of third party components as I have discovered there are a few that I would like to use. Sencha is much harder to learn then JQuery, but I believe in the long run it will worth it.

          Cheers Billy B

  9. Ram says

    Hi jorge,

    I have a global toolbar View and a controller for that and i am using this view as xclass in all other views and these views have their own controllers, So now my problem is i have only one back button in global toolbar, So how can i implement the back Logic Please help me solve this problem.

    Thanks
    Ram

    • says

      One way to do what you need is to, in the global controller, have a reference to each of the views that will need to change state when the back button is tapped. Another approach would be to have the global controller send a message to each of the child controllers, asking them to handle the “back” logic.

      • Ram says

        Hi Jorge,

        Thanks for ur reply i have only one back button in global controller how can i get a reference that in which view i am and how can i call the corresponding child controller ? Please help me to solve this problem.

        Thanks
        Ram

  10. Ram says

    Hi Jorge,

    Is there any other way adding views to Viewport the views adding to viewport in your case

    Ext.Viewport.add([home,secondView,thirdView,fourthView]);

    Is calling all the initialize functions of all views i want to call the initialize functions only when i move to their perticular views.

    Thanks
    Ram

  11. says

    I’m a designer learning JS and have settled on Sencha’s Architect 2 as the best environment for developing UX designs. I left Flash Catalyst after Adobe abandoned the product. HTML 5 will be around for a while and is a worthy platform for beginners like myself to learn. The drag and drop interface and configuration panel, exposing objects properties I think will draw many like myself interested in creative control of the entire experience. How about developing a series of tutorials geared towards beginners using Architect 2, focusing on the UX.

    Thanks!

    • says

      Thank you Charles. Architect is a very interesting tool. I will try to write about it as soon as I can. When people ask about it, I always recommend that they start by learning how Sencha Touch or Ext JS work. Only then can they take full advantage of Architect.

  12. paul says

    Really awesome post. It’s hard to find good articles on 2.0. I’m just starting with the sencha touch.

    I was wondering. How would it work if I wanted 2 controllers instead of 1.
    A controller for the NoteList and another for the NoteEditor.

    I’m expecting that the app.js, will only have initially loaded the controller of NoteList since it doesn’t need to load the NoteEditor controller.

    Would that be possible?

    Thanks,

    • says

      Thank you, Paul. Yes, it’s possible to have more than one controller. For small apps like this one, I usually start with one. As features are added to the app, I check to see if it makes more sense to distribute the responsibilities across multiple controllers.

      • Paul says

        Would you be able to write an article on this subject, like a more real world example with multiple controllers each controlling a specific view? I’m really curious to see how this would work. I tried myself and failed miserably. I’m at a point where I don’t really know how to load the second controller. For example, your notes app. You have the NoteList and the Notes controller. When user tap on an item it shows the NoteEditor. From there how could I load the NoteEditor controller? A way to make it work would be to have it in the app.js …. but that wouldn’t make much sense does it? Since it is only needed when the NoteEditor is visible. Loading the NoteEditor controller at the very beginning when it is not needed could cause a performance issue I would assume.

        Thanks,

  13. Philip A. says

    Can’t never lunch this tut. at all! Even I supplied all the required files not packed with this .zip as below. Could you tell me how to run this tut.?

  14. Tejas says

    Gr8 post , very helpful indeed!

    Any idea how to implement the autocomplete functionality in Sencha Touch 2 ? . I’m stuck with that for a while. Help will be very much appriciated.
    Thanks in advance

  15. Sandeep says

    As per sencha doc its a bad practice to define event listeners and handler in the view file when doing Ext.define. Any input on this?

        • says

          Thanks. First, I’d like the documentation to explain why it’s bad practice. That will help more than simply saying it is bad practice. Second, I think this refers to listeners that are intended to be instance-specific. In other words, listeners to public events of a class. If that’s the case, I agree. If you want to listen to events pubished a class, you should define the listeners when the instance is created, not when the class is defined.

          However, in the case of my tutorial, the listeners and the handlers you refer to are internal to the class. They simply serve to transform events in internal components of the class into public events of the class. Notice that the listeners for these public events, which I define in the controller, could be said to follow the “best” practice, as they apply to an instance of the class that fires the events.

  16. Sandeep says

    need help understanding itemID. Is it just like having an attribute like name and then searching it like

    container.query([name=”bla”]) or does itemId add any other features?

    • says

      The itemId config of a component, not to be confused with the id, is an index into the items collection of the Container that hosts such component. Therefore what you say is valid and useful to quickly find child components of any container within the container’s scope. Components in different containers can have the same id, which is useful when you create multiple instances of the same class, for example.

      • Sandeep says

        i see. that makes sense.. so rather than using a numeric index in the items array we are looking them up by a friendlier string. thx

  17. shiva says

    Hi Jorge,
    This is a wonderful article. If you could write about production deployment processes, like using the cache manifest and micro-loader, it would be great. Looking forward to your posts. Thank you. :-)

  18. patrick says

    Hey Jorge..thanx for the post…man i have a list of items with a disclosure on each list…..and what i want is to pass the id of an item on disclosure to the server so that i use the id on the sever side to query the database…..but i don’t know how to do it…..how can i post the id on itemDisclosure

  19. says

    Thanks for the nice tutorial!

    Just a thought, if you want the browser (and Android) Back buttons to work, routes are the way to go. At the top of the Notes controller, add the following:

    routes: {
    ”: ‘activateNotesList’,
    ‘notesEditorView': ‘showNoteEditorView’
    }

    The empty route specifies which method of the controller will be called when you load index.html without an anchor (e.g. when you launch the app); the “notesEditorView” route specifies which method will be called when you load the “notesEditorView” anchor (e.g. when you call redirectTo() to go to another view).

    After configuring the routes, replace the activateNoteEditor() method with the following 2 methods:

    activateNoteEditor: function(record) {
    var noteEditorView = this.getNoteEditorView();
    noteEditorView.setRecord(record);
    this.getApplication().redirectTo(‘notesEditorView’);
    },
    showNoteEditorView: function() {
    Ext.Viewport.animateActiveItem(noteEditorView, this.slideLeftTransition);
    }

    In activateNoteEditor(), you get a reference to the editor view and set its record. But instead of just showing the view, you call redirectTo(), which will redirect the browser to the anchor you specify. Since you’ve configured a route for that anchor, the showNoteEditorView() handler is then called, and that’s where you actually show the view in the UI. The difference can be observed in your browser’s address bar. You’re now adding to the browser history.

    The last step is to replace all calls to this.activateNotesList() with calls to history.back(). When you press the Back, Save or Delete buttons, history.back() will be called. The empty-anchor route is configured to call activateNotesList(), which shows the list view. This method will also be called if you press the browser or Android Back button.

    If you have a complex app with many depth levels and you want to efficiently support the Android Back button, this is a good way to go.

    Hope this helps!

  20. Daniel says

    Hello Jorge,
    I have the following error:
    Uncaught Error: Ext.setup has already been called before sencha-touch.js:8890
    Ext.setup sencha-touch.js:8890
    Ext.apply.application sencha-touch.js:9219
    (anonymous function) app.js:1

    app.js:1 is Ext.application({
    Hope for any hint. Thank you.

  21. Mayur Sirwani says

    I wouldn’t have learnt #sencha this beautifully if your tutorial wouldn’t have been there, this is 100x much better than tutorials on their website. You Rock!! \m/

  22. Sel says

    Hi,

    In many cases you have used “delegate” in the config. Can you point out where in the documentation “delegate” is defined? I am not able to find reference to it.

    Thanks

  23. Aditya Dinasty says

    nice tuts,,,,,,,, i read it many times
    try to understand concept of MVC, i had a little bit concern in calling MVC, coz it little bit different with EXT JS 2.2 i’ve learn, just like learn something new here ,,,,,,,

    wish i have your brain in me,,,,heeee

    great job
    tx

  24. joven says

    Jeorge i am very thankful for giving you this kind of tutorial. You are a great person and a generous one and a whole heart love for giving a knowledge for the other person who wants to learn this mobile application. People who loves to make mobile app and continuing to learn will never forget you how you help them on how they learn from the beginning. I hope that you will not change and thank you once again.

  25. v.f. says

    Ciao Jeorge, great tutorial, i want buy your book, but before i want ask you if it needs an update to sencha 2.1, the examples of your book work with st2.1?
    grazie

  26. George says

    Hi Jorge,
    I have written an email to you earlier. Nice tutorials you have got here on ST2.
    How about getting your ebook?
    Thanks.

  27. Rambabu says

    Hi Jorge,

    Nice tutorial and it is very helpful for beginners like me.
    I want to add container within another container programmatically first time it works great next time onwards it is not working any suggestion to resolve the issue.

  28. Student says

    Hi Jorge,

    You made excellent tutorials.

    I download the code and run on my PC and everything work like charm.
    But when I run the application on iPhone 4 or iPhone 5 using Google Chrome, there is no bottom bar in ‘Edit note’ screen.
    I have made ​​no changes to the code.
    Can take a look and tell me what causes it ?

    Thank you very much.

  29. Ron says

    Jorge,
    I’ve gone through your eBook and tried to run the production code, when I got this error:
    XMLHttpRequest cannot load file:///MyNotes/build/MyNotes/production/app.json?1368371697903. Cross origin requests are only supported for HTTP.

    Any ideas as to why?

    I also tried to install on android device. It installed and the main screen appears. The message No Notes appears, but the New button doesn’t work. Have you seen this before?

    Thanks,
    Ron

    • says

      This is happening because you are accessing the app through the file system, instead of through your local web server. The uri you type on your browser should start with http, something like this: http://mynotes …, where mynotes is the virtual folder where your app resides.

  30. Venkateshwar says

    Thank you for explaining the tutorial step by step.

    At every start of a function, you are calling “this.callParent(arguments)”. What does it actually supposed to do?

    I tried to understand from Sencha docs but it is confusing me.

    please help.

    • says

      No problem. For example, if you are inside a hypothetical constructor() method of a Widget class, calling callParent() will invoke the constructor method of the Widget’s parent class.

  31. Alan says

    Hi Jorge. I’m sorry if you already explained this somewhere already and I missed it. Can you talk about why you chose to have listeners defined in the view which in turn fire events for which the controller listens. This versus just listing for the tap event in the controller. Thanks! Alan

    • says

      There’s some discussion on this above. In short, this allows you to create a controller that does not know about the view’s internals. Therefore you can change the way the events are generated in the view without having to touch the controller, resulting in a more maintainable app. Of course it all depends on the size of the app and your personal preferences.

      • Alan says

        Thanks for the quick response. I like the concept. However, caution needs to be used with the way you’ve implemented it. When you define listeners in the config using Ext.define, you wipe out any other listeners you would normally inherit. This is because the config is merged with the extended class’s config. When the merge is done, your listeners property overwrites the extended class’s listener property. So in your example above, if you wanted to listen for the Panel’s initialize event in your controller, it would not work. For this reason it’s discouraged. And even though you may know what’s happening, if another developer came along to work on your code, they may not know this and will be scratching their head wondering why events aren’t working. So for maintainability it’s probably best not to do it this particular way. Perhaps there is another approach which solves both problems. Your thoughts?

      • Alan says

        According to what I’ve read, this would be the safest way to do it. You add the listeners during initialization…

        initialize: function() {
        this.down(“#backButton”).on(“tap”, onBackButtonTap);
        this.callParent();
        },

        onBackButtonTap: function () {
        this.fireEvent(“backToHomeCommand”, this);
        }

  32. Roy Varghese says

    Hi Jorge,

    I wanted to know how to minify you example application.

    I ran “sencha –debug app build testing” and saw a build folder in my app for your sample application. But if I try opening the built app in the browser, it throws the following error:
    Uncaught TypeError: Cannot call method ‘setRecord’ of undefined app.js:65910
    Ext.define.activateNoteEditor app.js:65910
    Ext.define.onEditNoteCommand app.js:65938
    Ext.define.doFire app.js:14650
    Ext.define.fire app.js:14577
    Ext.define.doDispatchEvent app.js:15075
    Ext.define.dispatch app.js:59369
    Ext.define.publish app.js:59443
    Ext.define.doFire app.js:14650
    Ext.define.fire app.js:14577
    Ext.define.doDispatchEvent app.js:15075
    Ext.define.dispatchEvent app.js:15056
    Ext.define.doFireEvent app.js:15416
    Ext.define.fireEvent app.js:15370
    Ext.define.onNotesListDisclose app.js:65770
    Ext.define.doFire app.js:14650
    Ext.define.fire app.js:14577
    Ext.define.doDispatchEvent app.js:15075
    Ext.define.dispatchEvent app.js:15056
    Ext.define.doFireEvent app.js:15416
    Ext.define.fireAction app.js:15401
    Ext.define.handleItemDisclosure app.js:58837
    Ext.define.doFire app.js:14650
    Ext.define.fire app.js:14577
    Ext.define.doDispatchEvent app.js:15075
    Ext.define.dispatch app.js:59198
    Base.implement.callParent app.js:4495
    Ext.define.dispatch app.js:59967
    Ext.define.doPublish app.js:60060
    Ext.define.publish app.js:59974
    Base.implement.callSuper app.js:4552
    Ext.define.publish app.js:60996
    Ext.define.onRecognized app.js:60981
    Ext.define.fire app.js:61368
    Ext.define.onTouchEnd app.js:62352
    Ext.define.invokeRecognizers app.js:61046
    Ext.define.onTouchEnd app.js:61234
    Ext.define.onEvent app.js:60951
    (anonymous function) app.js:312

    What is the right way to go about this?

    Regards,
    Roy

Leave a Comment

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