Home > Application Development, Sencha Touch > Sencha Touch MVC Application – Part 2

Sencha Touch MVC Application – Part 2

February 27, 2011 Leave a comment Go to comments

In my previous post, I shared presentation on Sencha Touch MVC. That’s all the “talk”, it is time to see it in action. This short tutorial will walk through the implementation of Sencha Touch MVC application. I revamped my old (and long forgotten & messy) USGS apps as example. Before I dive into the code, I will share how to structure the apps.

Structuring Your Apps

Firstly, create the folder structure to support the MVC application. The figure below shows the basic structure required and the codes are available here. I personally think this is a good practice and can be use as coding standard and guideline for Sencha Touch apps. In addition to that, you may want to standardize the file & object naming convention for the controllers, views and data models.

  • m.usgs – the root folder for the application
    • app – folder for your sencha touch code, containing app.js and folders for controllers, models and views.
    • lib – folder for sencha library and other plug-in. (in my example, I moved the sencha library out for sharing with other apps)
    • resources – folder for web resources such as css & images.
    • index.html – the main page to launch the apps.
Sencha Touch - Structuring Your Apps for MVC

Sencha Touch - Structuring Your Apps for MVC

Coding

Step 1: Instantiating the Application

Create app/app.js and add the basic launcher for an Ext.Application instance. In the example it will be namespaced as “usgs”.

Ext.regApplication({
    name: "usgs",
    launch: function() {
	this.views.viewport = new this.views.Viewport();
    }
});

Create a main Viewport view which will house the pages required for displaying USGS Earthquake data. Here we place references to the card instances in the “usgs.views” namespace, so that we can refer to them explicitly in our page flow.

usgs.views.Viewport = Ext.extend(Ext.Panel, {
    fullscreen: true,
    layout: 'card',
    cardSwitchAnimation: 'slide',
    initComponent: function() {
        //put instances of cards into app.views namespace
        Ext.apply(usgs.views, {
        	usgsMenu: new usgs.views.UsgsMenu()
        	,usgsList: new usgs.views.UsgsList()
        	,usgsMap: new usgs.views.UsgsMap()
        });
        //put instances of cards into viewport
        Ext.apply(this, {
            items: [
                usgs.views.usgsMenu
                ,usgs.views.usgsList
                ,usgs.views.usgsMap
            ]
        });
        usgs.views.Viewport.superclass.initComponent.apply(this, arguments);
    },
    layoutOrientation : function(orientation, w, h) {
        usgs.views.Viewport.superclass.layoutOrientation.call(this, orientation, w, h);
    }
});

Step 2: Modelling Data

Like any MVC-based application the next step is to prepare for the data model. In the models folder, create a definition of the USGS model. The data structure match the content structure is USGS website. In order to demonstrate the use of JSON, I used YQL to convert the RSS data from USGS.

Ext.regModel("usgs.models.UsgsData", {
    fields: [
      {name: 'id', 		type: 'int'},
      {name: 'title', 		type: 'string'},
      {name: 'description', 	type: 'string'},
      {name: 'link', 		type: 'string'},
      {name: 'pubDate', 	type: 'date'},
      {name: 'lat', 		type: 'string'},
      {name: 'long', 		type: 'string'}
    ]
});

Create a simple data store to retrieve the data. For cross domain ajax, set the proxy type to ‘scripttag’. It also handles the callback. :)

usgs.stores.usgsData = new Ext.data.Store({
    model: 'usgs.models.UsgsData',
    proxy: {
    	type: 'scripttag',
    	url: 'http://query.yahooapis.com/v1/public/yql',
    	extraParams: {
    		format: 'json'
    	},
	  	reader: {
	  		root: 'query.results.item'
	  	}
  	}
});

Step 3: Creating the Views

The data is ready, now we start to create the view for the USGS data. It allows us to test the data store as well. In this example, I have 3 views:

  1. USGS Menu – this view contains list of data we can retrieve from USGS
  2. USGS List – based on the selected menu, this view shows the list of USGS earthquake data.
  3. USGS Map – this view shows the map where the earthquake occurred.

Here I will just show one of the view. Ignore the call to the controller (Ext.dispatch(…)) for now, wire the view when the controller is ready.

usgs.views.UsgsList = Ext.extend(Ext.Panel, {
    dockedItems: [{
        xtype: 'toolbar',
        title: 'USGS',
        dock: 'top',
        items: [{
        	xtype: 'button',
          text: 'Back',
          ui: 'back',
          handler: function() {
            Ext.dispatch({
                controller: usgs.controllers.usgsController,
                action: 'backToIndex'
            });
          },
          scope: this
        }]
    }],
    items: [{
        xtype: 'list',
        emptyText   : 'No data available.',
        store: usgs.stores.usgsData,
        itemTpl: '{title}',
        onItemDisclosure: function (record) {
            Ext.dispatch({
                controller: usgs.controllers.usgsController,
                action: 'showMap',
                data: record.data
            });
        },
        grouped: false,
        scroll: 'vertical',
        fullscreen: true
    }],
    initComponent: function() {
        usgs.views.UsgsList.superclass.initComponent.apply(this, arguments);
    }
});

Step 4: Building the Controller

We reached the last step and this is the point where all the action and view wired together. If you look back at the view, the handler dispatches specific action to the controller. The actions are defined in the methods below and the controller will then manage the page flow by activating the card. Switching page is done by calling the setActiveItem() and it is applicable because my Viewport is using card layout.

usgs.controllers.usgsController = new Ext.Controller({

    index: function(options) {
        usgs.views.viewport.setActiveItem(
            usgs.views.usgsMenu, options.animation
        );
    },

    showMap: function(options) {
        var data = options.data;
    	usgs.views.usgsMap.addMap(data);
        usgs.views.viewport.setActiveItem(
           usgs.views.usgsMap, options.animation
        );
    },

    showUsgsList: function(options) {
        var id = parseInt(options.id);
    	usgs.stores.usgsData.getProxy().extraParams.q = usgs.query[id].q;
    	usgs.stores.usgsData.read();
        usgs.views.viewport.setActiveItem(
            usgs.views.usgsList, options.animation
        );
    },

    backToIndex: function(options) {
        usgs.views.viewport.setActiveItem(
            usgs.views.usgsMenu, options.animation
        );
    },

    backToUsgsList: function(options) {
        usgs.views.viewport.setActiveItem(
            usgs.views.usgsList, options.animation
        );
    }
});

If you are not using card layout then the controller will need to render the UI component. In the sample code below the view is rendered using the Ext.Controller render() and listener is added to catch the event bubble from the view.

list: function() {
    this.listView = this.render({
        xtype: 'myMainPanel',
        listeners: {
            scope : this,
            filter: this.onFilter,
            selectionchange: this.onSelected
        }
    }, Ext.getBody()).down('.dataList');
}

The screenshots below are the result from the above tutorial.

Implementing the structure and MVC into Sencha Touch definitely helps to simplify my design. I can easily implement the page flow and not end up with spaghetti code. It also helps to separate the view, data and controller and make the code more maintainable.

Hope it is useful and happy hacking!

About these ads
  1. Pedro Leite
    February 27, 2011 at 6:21 pm | #1

    Nice post….I’m currently reading up on the Sencha Touch FW to develop a web app using MVC…so your article just came in handy :) keep up the good work!

    • wowi
      February 28, 2011 at 2:12 am | #2

      Thanks.

  2. Larry Q
    March 8, 2011 at 12:41 am | #3

    Very nice article. May I ask why you removed the map in your addMap function, after checking whether the first component on the page was a box/map? (see UsgsMap.js source)

    I had to do the same thing to get my MVC app working– otherwise, whenever the controller switched from the map panel to another, the map ‘remained’ onscreen and obscured my other panel. So your code helped me in this regard, but I’m not sure why it’s needed? A bug in Sencha, or is there some other reason?

    • wowi
      March 8, 2011 at 11:46 am | #4

      Hi Larry,
      Thanks.

      I removed the map to manage the memory usage. The panel maintain each item in a collection. Each time add(map) is called, the map object will be appended to the collection. Initially I just call the doLayout() after add(), the screen get refreshed but I realize the collection keep growing. That’s not good, so I decided to remove first b4 adding.

      Checking the box/map may not be the best way. In this case my panel only show a single map, so I can safely remove the first item in the collection. Alternatively, you can get the map & map id after calling add() and track it.

      Hope that explain why I remove the map.

      Cheers.

  3. Vitaly
    April 4, 2011 at 3:25 pm | #5

    Please, share your app code!

  4. Vitaly
    April 5, 2011 at 9:34 am | #8

    Could you help me please?
    I am looking for way to pass variable to View and then use it.
    I am calling View from controller like this (it’s an action):

    show: function(options) {
    var userdata = options.userdata;
    this.view = this.render({
    xtype: ‘cmTeamuser’
    });
    }

    So how to pass userdata to cmTeamuser view?

    • wowi
      April 5, 2011 at 12:47 pm | #9

      Not sure what is the view implementation look like. But I can think of 3 ways, both are used in my sample code.
      1. to have a method in the view that allow you to set the value from the controller, e.g.

      showMap: function(options) {
      var data = options.data;
      usgs.views.usgsMap.addMap(data);
      usgs.views.viewport.setActiveItem(
      usgs.views.usgsMap, options.animation
      );
      },

      2. if the item in the view allows the use of ‘store’, you can load the store before calling the view. e.g.

      showUsgsList: function(options) {
      var id = parseInt(options.id);
      usgs.stores.usgsData.getProxy().extraParams.q = usgs.query[id].q;
      usgs.stores.usgsData.read();
      usgs.views.viewport.setActiveItem(
      usgs.views.usgsList, options.animation
      );
      },

      3. if the view is a form, you can pass the model object to the form. For this to work the data field must match the form fields name e.g.

      formView.load(userdata);

      Hopefully that helps.

      • Vitaly
        April 5, 2011 at 12:59 pm | #10

        Oh. Thank you. And sorry – View in my implementation is:

        Ext.views.ViewName = Ext.extend(Ext.Panel, {});

        I follow architecture as in Kiva example.

      • wowi
        April 5, 2011 at 1:31 pm | #11

        ic.. i checked out the sample. i believe the data is passed by calling view.setLoan(options.instance), where options.instance is the loan record. similar to the option (1) in my previous reply.

  5. ajg
    May 8, 2011 at 11:40 pm | #12

    Following your setup…i have a form in one of the views. For one of the fields i want to get the current date…

    I have the following function for getting that date…

    function getDate(){
    var currentTime = new Date();
    var month = addZero(currentTime.getMonth() + 1);
    var day = addZero(currentTime.getDate());
    var year = currentTime.getFullYear();
    return(day + “/” + month + “/” + year);
    }

    where should i put this function so it can be used by many views and how would i call it for my form??

    Something like this??

    xtype: ‘textfield’,
    name: ‘date’,
    label: ‘Date’,
    value: getDate()

    • wowi
      May 9, 2011 at 11:15 am | #13

      There is no fix rule on this, my suggestion is to separate .js file e.g. util.js. Include the file the same as app.js.
      if you prefer textfield over datefield, you can extend the textfield to create your own date. Similarly, include it in the same way as app.js

      just a quick sample.
      Ext.form.MyDateTextField = Ext.extend(Ext.form.TextField, {
      initComponent : function(){
      Ext.form.MyDateTextField.superclass.initComponent.call(this);
      this.setValue(Ext.util.Format.date(new Date(), 'd/m/Y'));
      }
      });
      Ext.reg('mydatefield', Ext.form.MyDateTextField);

      use it in this way
      {
      xtype: 'mydatefield',
      fieldLabel: 'Date',
      name: 'date'
      }

      hope that helps.

  6. ajg
    May 10, 2011 at 12:30 am | #14

    Awesome thanks so much! That works. But now i am on to bigger things :)

    Sorry for this being off topic of your post. But I want to put a list in one of my views. I cannot get this to work and was wondering if you have any insight. Any help would be greatly appreciated Here is my view:

    Toolbar.views.Contact = Ext.extend(Ext.List,{
    fullscreen: true,
    iconCls: ‘home’,
    layout: {
    type: ‘vbox’,
    align: ‘center’,
    pack: ‘center’
    },
    items:[{
    xtype: "list",
    store: ListDemo.ListStore
    }],

    initComponent: function() {
    Toolbar.views.Contact.superclass.initComponent.apply(this, arguments);
    }
    });

    And here is my date store

    Ext.regModel(‘Contact’, {
    fields: ['firstName', 'lastName']
    });

    ListDemo.ListStore = new Ext.data.Store({
    model: ‘Contact’,

    data: [
    { firstName: "Domino", lastName: "Derval" },
    { firstName: "Elektra", lastName: "King" },
    { firstName: "Fiona", lastName: "Volpe" }
    ]
    });

    • wowi
      May 11, 2011 at 12:04 pm | #15

      just got time look at it. The Toolbar.views.Contact extend Ext.List, so you do not need to define items property anymore and you must define item template also, e.g.
      Toolbar.views.Contact = Ext.extend(Ext.List,{
      fullscreen: true,
      iconCls: 'home',
      layout: {
      type: 'vbox',
      align: 'center',
      pack: 'center'
      },
      store: ListDemo.ListStore,
      itemTpl: '

      {firstName} {lastName}

      ',

      initComponent: function() {
      Toolbar.views.Contact.superclass.initComponent.apply(this, arguments);
      }
      });

      noticed that you used namespace, you will need to register them first
      Ext.ns('ListDemo', 'Toolbar.views');

      That work for me, hope it helps.

  7. ajg
    May 12, 2011 at 2:03 am | #16

    No that doesn’t work. It does not seem to like the way i have my store set up.

    Does this store look right??

    Ext.ns(‘ListDemo’, ‘Toolbar.views’);

    Ext.regModel(‘Contact’, {
    fields: ['firstName', 'lastName']
    });

    ListDemo.ListStore = new Ext.data.Store({
    model: ‘Contact’,
    data: [
    { firstName: "Domino", lastName: "Derval" },
    { firstName: "Elektra", lastName: "King" },
    { firstName: "Fiona", lastName: "Volpe" }
    ]
    });

  8. ajg
    May 12, 2011 at 2:14 am | #17

    oh never mind. I got it. If i combine the section i posted above with my View, it works.

    Thanks for the help.

  9. kprk
    May 18, 2011 at 8:22 am | #18

    Hi, wowi,
    I really loved your work . I got what I was looking for. I am going to start one sencha touch application at my office, but I am not sure how to start as this is my first experience with sencha touch, could you please post or send me email how to do set up for sencha application and how to test. I am planning on android.

    thanks in adv,
    kprk

    • wowi
      May 20, 2011 at 11:35 am | #19

      thanks. i will email you shortly.

  10. Jason
    May 27, 2011 at 3:07 pm | #20

    Thanks for this! This has been the most useful example I’ve found while trying to learn Sencha Touch. Please do some more!

  11. David
    June 2, 2011 at 2:53 pm | #21

    Thanks a lot for this awesome example! i’m working in a finder aplication with this concept. Just one tip, in UsgsMap class, at the end, when adding the listener for the map bubble marker, you should use ‘mousedown’ instead of ‘click’ event, touch devices like iPad/iPhone doesn’t recognize the click event.

    Regards!

    • wowi
      June 6, 2011 at 12:04 pm | #22

      hi, thank for the feedback. will try it out.

  12. neverMind
    June 7, 2011 at 6:04 pm | #23

    Thank you very much for this excellent mvc structure.
    Please I need to know how to add a permanent “tabPanel” using MVC, please help

    • wowi
      June 8, 2011 at 12:54 pm | #24

      hi,
      did a quick modification on code but din have time to do the write up. the changes are:
      1. the viewport is now base on the tabpanel
      2. add a new tab view for the usgs and then add it into the viewport
      3. the controller is intended for the usgs tab only, so the code is modifiy to switch view withtin the tab.

      i uploaded the sample and available @ http://bit.ly/mNRQZ5. Hope that help

  13. JS
    June 8, 2011 at 7:32 pm | #25

    Thanks for a very useful tutorial. It would have been helpful in the text if you could indicate the file names into which the above sample code should be placed. I ended up looking at the Git Hub sources to figure out what files and which code was in it.

    Now that it is working trying to understand how it works….

  14. Morgan Arvander
    June 8, 2011 at 9:51 pm | #26

    I can’t get it to work on Android. Have you tried it yourself?

    • wowi
      June 9, 2011 at 2:05 pm | #27

      hi… What browser r u using? Sencha Touch is primarily based on WebKit, so it can support only WebKit-based browsers.
      I have tried in iPhone and got my friend to try on default android browser.

      • Morgan Arvander
        June 9, 2011 at 8:15 pm | #28

        I’m using the default browser I guess, I haven’t installed any other browser.

        I have tried the MVC sample from Sencha as well as my own project. Both projects works fine on the iPhone but not on Android.

        If I try to run the Contact sample from Sencha in Chrome it works if i comment out the statement
        if (!device || !this.launched) {return;}

        It turns out that the device variable is null but I can’t find any documentation regarding this variable.

      • wowi
        June 10, 2011 at 2:23 am | #29

        My sample work on Chrome (desktop) as well. Not sure whether or not different version of the Android has different browser. I heard the phone manufacturer can modify the default browser, but I cannot confirm.

        Let’s see what sencha will respond to your question.

      • Morgan Arvander
        June 9, 2011 at 8:45 pm | #30

        BTW, I have created a post at Sencha where an example application for Android has been uploaded.

        http://www.sencha.com/forum/showthread.php?136331-The-contact-MVC-sample-fails-on-Android

  15. kprk
    June 9, 2011 at 7:42 pm | #31

    Hi Wowi, could you please how to use webkit, I have been to website . do I need to install nightly build ? and how am I going to place my code ? could you please the guideline on this ? I know this could be basic qn but I am new bie and will start my app soo n :)

    kprk..

    • wowi
      June 10, 2011 at 2:19 am | #32

      hi… Some of the browser like Safari or Chrome uses webkit engine. Thus the webkit specific can run on those browser and you don’t have to install it separately. e.g. if you are using webkit specific css attribute, you can just test it on Safari or Chrome.

  16. June 30, 2011 at 1:16 pm | #33

    Thanks for the great article! Could any one share the complete Android project folder??

  17. neverMind
    July 7, 2011 at 6:58 pm | #34

    Hi it’s me again,
    I like the way you code withdout reg, but in the tabs example there is an problem when content overflowed and vertical slider

    • wowi
      July 8, 2011 at 12:01 am | #35

      thanks for the feedback. btw, what are you displaying on each of the tabs? may be I can simulate and see if I can fix my sample.

  18. August 16, 2011 at 8:30 am | #36

    thanks,

    But what about performances (User experiance) ?

    • wowi
      August 22, 2011 at 12:59 pm | #37

      hi, r u referring to the network round trip from having too many js? if yes, one way to merge them as part of the build process…

  19. JP
    August 21, 2011 at 10:01 pm | #38

    Thanks for doing this. This is really helpful. I was working with your example code modified for a TabPanel. I’d like to skip the first Menu and load the list when the app launches and then tap to load map. Is that possible using this structure?

    Thanks

  20. zibzulander
    August 24, 2011 at 9:22 pm | #41

    Excellent tutorial. Will someone explain the argument called “argument” in line 20 of the Viewport file? What type is it? Where does it come from? It makes very little sense to me.

  21. Tony Taylor
    August 27, 2011 at 5:45 pm | #42

    This tutorial is great stuff! It really helped me get a better understanding of sencha and mvc and I thank you for that.

    I’ve been recently trying to employ BDD on my app that’s inspired by this tutorial (using Jasmine which the Sencha team seems to like) and I found that the framework doesn’t get along with objects with dot notation in their names. (i.e. – new usgs.views.UsgsMenu() will always return an error, but new UsgsMenuView() works fine)

    What’s your experience with using testing frameworks with Ext/Sencha?

  22. John James
    September 5, 2011 at 9:44 pm | #43

    Thank you.
    This is a better example than Sencha has on their tutoriol which does not explain the MVC approach or only does a superficial job.
    Great work.

  23. TRL
    September 14, 2011 at 2:54 pm | #44

    Thank you so much for this example. This is by far the best I’ve found and the most applicable for my needs.

  24. James Henderson
    October 4, 2011 at 11:26 am | #45

    Hi – thanks – this is really interesting – one question… in the index.html file, why have you put the script files inside the body tag, and also, why are they inside divs?

    • wowi
      October 4, 2011 at 11:45 am | #46

      hi, no particular reason. just making the example readable. you may put the script inside head also.

  25. Jason
    October 17, 2011 at 8:14 pm | #47

    Any chance of seeing the USGS app updated for Sencha Touch 2.0? I noticed they are handling a few things differently in 2.0.

    Your examples have been the best resource I’ve found for learning!

  26. December 8, 2011 at 4:35 pm | #48

    I’d also like to see the USGS app in ST2.0. Any chance of that coming soon?

    • wowi
      December 13, 2011 at 9:54 pm | #49

      lately busy with project at work, will find time to upgrade it. ty :)

  27. December 13, 2011 at 8:47 am | #50

    While building installable file using phone gap I am getting error in app.js at this.views.viewport = new this.views.Viewport();

    as ReferenceError: Can’t find variable: app at file ://android_asset/www/app/app.js:9
    my code in app.js is

    Ext.regApplication({
    name: ‘c4g’,
    launch: function() {
    this.launched = true;
    this.mainLaunch();
    },
    mainLaunch: function() {
    if (!device || !this.launched) {return;}
    this.views.viewport = new app.views.Viewport();
    }
    });

    • wowi
      December 13, 2011 at 9:57 pm | #51

      it should be this.views.viewport = new this.views.Viewport();
      because your app is registered as ‘c4g’, subsequently you have to refer to ‘c4g’ instead of ‘app’.

  28. Jay
    April 20, 2012 at 7:54 pm | #52

    Really great post. How hard would it be to make this work with Sencha Toucha 2.0?

  29. prabhakaran
    August 7, 2012 at 12:17 pm | #53

    i am beginner of sencha I didnt know how to start as this is my first experience with sencha touch, could you please post or send me email how to do set up for sencha application and how to test. I am planning on android.

  30. August 13, 2013 at 8:00 pm | #54

    This design is incredible! You obviously know how to keep a reader amused.
    Between your wit and your videos, I was almost moved to start my own
    blog (well, almost…HaHa!) Wonderful job. I really loved what
    you had to say, and more than that, how you presented it.
    Too cool!

  31. August 15, 2013 at 10:53 am | #55

    Simply want to say your article is as astounding.
    The clearness to your publish is just spectacular and that i can suppose you’re knowledgeable in this subject. Fine together with your permission let me to grab your feed to keep updated with drawing close post. Thanks one million and please continue the enjoyable work.

  32. August 15, 2013 at 7:28 pm | #56

    Link exchange is nothing else except it is simply placing the other person’s webpage link on your page at proper place and other person will also do similar in favor of you.

  33. August 21, 2013 at 2:30 am | #57

    What i do not realize is in reality how you are not
    actually much more neatly-liked than you may be now.
    You’re very intelligent. You already know therefore significantly relating to this subject, produced me personally believe it from a lot of various angles. Its like women and men aren’t interested except it’s one thing to accomplish with Lady gaga! Your individual stuffs excellent. Always deal with it up!

  1. April 29, 2011 at 6:32 am | #1
  2. October 25, 2011 at 12:19 am | #2
  3. January 12, 2013 at 5:18 pm | #3

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: