How to Create a Sencha Touch 2 App, Part 3

In this third part of this tutorial on how to create a Sencha Touch application, we are going to create the Note Editor View. This is the View that will allow our users to create, edit and delete notes.

When we finish this article, our application will have the ability to create notes, and edit existing notes. Let’s get started building the Note Editor View.

Creating a Form Panel in Sencha Touch

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

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

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

        this.callParent(arguments);

    }
});

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

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

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

        this.callParent(arguments);

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

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

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

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

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

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

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

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

});

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

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

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

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

Rendering a View In Sencha Touch

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

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

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

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

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

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

});

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

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

onNewNoteCommand: function () {

    console.log("onNewNoteCommand");

    var now = new Date();
    var noteId = (now.getTime()).toString() + (this.getRandomInt(0, 100)).toString();

    var newNote = Ext.create("NotesApp.model.Note", {
        id: noteId,
        dateCreated: now,
        title: "",
        narrative: ""
    });

    this.activateNoteEditor(newNote);
}

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

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

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

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

activateNoteEditor: function (record) {

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

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

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

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

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

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

Making The Application Aware Of A View

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

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

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

Ext.application({
    name: "NotesApp",

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

    launch: function () {

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

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

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

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

Editing A Record Rendered In A Sencha Touch List

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

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

onEditNoteCommand: function (list, record) {

    console.log("onEditNoteCommand");

    this.activateNoteEditor(record);
}

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

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

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

Using A LocalStorage Proxy In Sencha Touch

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

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

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

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

Ext.define("NotesApp.store.Notes", {
    extend: "Ext.data.Store",
    requires:"Ext.data.proxy.LocalStorage",
    config: {
        model: "NotesApp.model.Note",
        proxy: {
            type: 'localstorage',
            id: 'notes-app-store'
        },
        sorters: [{ property: 'dateCreated', direction: 'DESC'}]
    }
});

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

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

Adding Event Listeners To A Sencha Touch Controller

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

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

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

Now we can define onSaveButtonTap() as follows:

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

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

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

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

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

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

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

onSaveNoteCommand: function () {

    console.log("onSaveNoteCommand");

    var noteEditor = this.getNoteEditor();

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

    // Update the current note's fields with form values.
    currentNote.set("title", newValues.title);
    currentNote.set("narrative", newValues.narrative);

    var errors = currentNote.validate();

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

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

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

    notesStore.sync();

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

    this.activateNotesList();
}

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

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

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

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

Model Validation In Sencha Touch

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

var errors = currentNote.validate();

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

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

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

Saving Data Using LocalStorageProxy

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

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

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

notesStore.sync();

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

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

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

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

Returning To The Main View

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

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

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

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

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

Summary

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

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

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

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

Stay tuned!

Downloads

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

The Entire Series

Want To Learn More?

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

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. Hey Jorge,

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

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

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

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

  2. BrownieBoy says:

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

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

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

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

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

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

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

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

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

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

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

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

    this.setRecord(this.getRecord());

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

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

  5. Hi Jorge,

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

    thanks
    waheed

    • Hi Waheed. I will add the delete feature in the next part of the tutorial.

      • thanks for your immediate reply.

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

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

        adding deleteButtonCommand: “ondeleteButtonCommand” in the controller .

        ondeleteButtonCommand: function (list, record) {

        this.activateNotesList();
        }

        any suggestions…..

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

          • thanks you very much. you are rock star.

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

            ondeleteButtonCommand: function () {

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

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

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

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

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

            thanks
            waheed

      • Hi jorge

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

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

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

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

        could you please help me.

        thanks in advance.

        • Flush your browser’s cache and try again.

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

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

  7. Flushing the cache does fix the error, is that the only way to fix it?

  8. Hi Jorge -

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

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

    Thanks for the great details.

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

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

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

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

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

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

      • Expect more examples from you

      • Hello Jorge,

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

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

        Please provide example. It would be really helpful.

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

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

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

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

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

  15. Hi,

    i like your structure of work.

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

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

    saveCommentCommand: function () {

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

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

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

    this function is not save the comment.

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

    Please give me suggestion

    • Make sure the Comment store is defined.

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

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

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

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

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

  16. Hi Jorge,

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

    Do you have any quick hints for me?

    Thanks a lot in advance!

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

      sorry for the inconvenience.

      Keep it up!

      • Hi Cliff,

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

        Thanks in advance for your help.

      • Hi cliff same problem, but not solutions, how did you fix it? thanks.

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

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

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

          Greetings and thanks to Jorge by this way :-)

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

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

            Everything works ! :)

  17. Hi Jorge,

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

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

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

    config: {

    items: [

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

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

    Thanks in advance!

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

  18. Alesha Mary Milred Fernandes says:

    Hi Jorge,

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

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

    • Tina, I’m not sure I completely understand. Can you post a short example?

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

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

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

        We commit the record like this in the Controller:

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

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

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

    • CP3,

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

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

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

  21. Hi Jorge,

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

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

    on this line code:

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

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

    Thx so much!

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

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

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

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

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

    },

  24. Jorge -

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

    Thanks,
    Tim

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

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

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

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

    Any ideas on how to address that?

  26. hi, getNoteEditor() where is this function defination? how to get a view object in controller?

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

  27. Strange error I’m getting:

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

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

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

    Any comments? Hints?

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

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

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

    },]
    }]
    });

    STORE CODE:

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

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

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

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

    }
    }

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

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

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

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

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

    • Thank you for buying the book!

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

  31. Hi Jorge,

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

    regards
    Meex

  32. ankit nanda says:

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

  33. Carlos Torres says:

    Hi Jorge,

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

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

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

    Any ideas how to fix this?

    Thanks in advance.

  34. Hello, I want to konw if a carousel contains a carousel, how can I accomplish the actions of wiping

  35. hi,
    Learnt a lot from ur tutorial…
    thanks buddy..

  36. These tutorials have been great so far.

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

  37. Meenakshi says:

    Hi,

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

    Can you please help.

  38. Thanks for tuto

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

Speak Your Mind

*