EN   |  RO

Online Solutions Development Blog   |  

RSS

Sencha Touch TabBar in a MVC structured application

posted by CAM,
Categories: JavaScript, Mobile Development
Tags , , ,

While I’m working on the 4th part of the Creating a Sencha Touch MVC application from scratch  series, I’ve decided to write this little article because the feedback received indicated that this would be useful for some of you.

Many Sencha Touch developers tried or want to use the TabPanel component in a MVC structured application  to navigate between different routes (controller/action pair), but this component is not intended for this purpose.

The way to do it is to dock a TabBar to the bottom or to the top of the application’s viewport and handle all interaction with the tab bar buttons ourselves.

To make it easier and to be reusable in other projects, I’ve extended the TabBar component into a class that handles all the “MVC stuff” automatically, like redirection to controller actions and setting the active tab bar button based on the current application route.

Here’s how to use it:

1. Download TabBarMvc.js and add it to you project; a good place for it is in the app/views folder. Don’t forget to include it in you index.html file:

...
<!-- VIEWS -->
<script type="text/javascript" src="app/views/TabBarMvc.js"></script><script type="text/javascript" src="app/views/Viewport.js"></script>
...

2. Add the component as a docked item to the viewport view like this:

dockedItems: [
    {
    	xtype: 'TabBarMvc',
    	items: [
	        {
	        	text: 'Home',
	        	iconCls: 'home',
	        	route: 'Home/index', // custom property of the TabBarMvc component
	        },
	        {
	        	text: 'About',
	        	iconCls: 'info',
	        	route: 'Home/about', // custom property of the TabBarMvc component
	        },
        ],
    },
],

As you can see the extended TabBar component has a new property named route. Based on this property the TabBarMvc will redirect to the right controller action.

Here’s a screenshot:

Update:

TabBarMvc will automatically send a reverse animation to the controller action when you click/tap a button that’s to the left of the currently activated one.
To make use of this feature you should do something like this in the controller action:

index: function(options)
{
    ...

    this.application.viewport.setActiveItem(this.indexView, options.animation);
},

The default animation is a slide. To change this, you can set the switchAnimation property of TabBarMvc to a different effect like this:

{
	xtype: 'TabBarMvc',
	switchAnimation: 'slide',
	...
}

or like this:

{
	xtype: 'TabBarMvc',
	switchAnimation: {type: 'slide'},
	...
}

Hope you’ll find the TabBarMvc component useful.

You can download an example project from here.

This is the first article that was written as a result of the feedback received so if you have anything to say don’t hesitate to add your contribution.

If you liked this post
you can buy me a beer

71 Responses to Sencha Touch TabBar in a MVC structured application

  1. I’m sure almost everyone have had this problem when using TabPanel with controller. Spot on for addressing this issue. Keep up the great work!!!

  2. Absolutely great, it works!

    oh, I discovered something funny…

    It appears if I use About

    The action takes me to the about page, but the active icon on the bottom is not moving, the “home” btn is still active and the “about” one is not.

    Any way to solve this small visual issue? :P

    • Thanks for spotting this issue. I’ve fixed it and now the files are updated.

      • No problem, I have applied the modifications and its working just great.

        Thanks a lot, you have make my day.

        I was having a difficult time trying to deal with this and this work of yours was the perfect solution.

        FYI, I have added your site as part of my special bookmarks collections. :P

        • I am glad that you found our blog useful. Stay tooned, there are lots of other interesting things to come.

          • I’m back for more knowledge.

            I’m trying to build a couple of forms. No problem there. My current issue I don’t know how to submit the form data to a php page to process the info. I have the fields and the the submit button created, but no matter how much I try, after pressing the btn, nothing happens. I don’t know what I’m doing wrong.

            Could you provide some guide and maybe a working example if possible…

            Thanx in advance.

          • Allan Sormani S.

            The guest book link did help me a lot.

            But it appears I will need to create the form fields dynamically. Im getting the information needed from a Multidimensional Array. I’m converted this super array into a Json Obj.

            I know sencha can read this kind of objects and have read several blogs and forum posts from the sencha website. But there is no easy to understand example on how to read/parse a Json object based on a multidimensional array.

            One of the things I do know, the Json data needs to be stored in the browser and then used. I’m kind of lost on this process.

            I just want to use the Json data to be able to create a form on the fly, at least the form elements (textfields, radios, checkbox, text areas, hidden fields, etc…) and all the possible attributes.

            All this its specified on the Json Obj.

            If you or someone else could point me to the right direction that could help me a lot.

            I think someone said something about a store inside sencha… hmm I don’t know for sure.

            Thanx in advance.

          • Allan Sormani S.

            Here is an example of my Multidimensional Array and the same data as a Json Obj…

            http://www.magvistudios.com/devFolder/megaArrayJsonObj.php

          • I’m also using the demo that comes inside the sencha (examples/kitchensink/ DATA – JSON P), the one of the weather.

            At the end on this example they are using a “new Ext.XTemplate”

            So they are defining a custome html template.

            I want to do the same thing with my own Json Obj. using the “Ext.extend(Ext.form.FormPanel …”

            It’s possible!? any way to doit?

  3. Guys great tutorials. Could you explain how to get more buttons on the panel, as the “about” one?

    I will appreciate it.
    Todos.

    • You can have more buttons on the tab bar by adding objects to the items array of the docked TabBarMvc:

      xtype: 'TabBarMvc',
      items: [
          {
          	text: 'Home',
          	iconCls: 'home',
          	route: 'Home/index', // custom property of the TabBarMvc component
          },
          {
          	text: 'About',
          	iconCls: 'info',
          	route: 'Home/about', // custom property of the TabBarMvc component
          },
          // the new button
          {
          	text: 'Search', // the label on the new button
          	iconCls: 'search', // "A css class which sets a background image to be used as the icon for this button"
          	route: 'Home/search', // the route you want
          },
      ],
      
  4. Do you mean that is not possible with this structure? “but this component is not intended for this purpose.”…

    Thanks in advance.

    • You can use the TabPanel within an MVC structured application, but only to switch between different views added to it and not to call a controller action.

  5. Guys, I figured it out. I just needed to place my anchor tags <a> right.

  6. Yes CAM like the one in part 3, thank you for your answers. My application is working pretty good so far and with a very nice structure thanks to your examples.
    But now I have an issue, that I haven’t been able to solve myself, is about the back button.
    I made some other panels and these panels have childrens. The way the back button is implemented now I go back to the ‘index’ or any other panel that I specify.
    Is it a way to make the back button to go to the previous panel, instead of to hard code it?

    I was thinking using an array and using .setActiveitem but I haven’t make it work yet. And also I will rather use the Ext.dispatch with its elegant “controller, action, historyUrl” if it would be possible.

    var stack = new Array();
        stack.push(App.views.Homelands, App.views.Homesoes, App.views.Homeluften); 
    
    backBtn.setHandler(function(button, event){
            	stack.pop();
            	App.views.Child.setActiveItem(stack.pop(), {type: 'slide', direction: 'right'});

    Do you have any suggestions?

    • With this “back button issue” I also had to deal in my projects.

      Besides a possibility to go back without “hard coding”, there is also the need to animate the screen with the appropriate effect.

      Along the way I’ve started to create a solution that works almost automatically. I will share the code with you in a new article when I am satisfied with the solution.

  7. Just one more note of thanks!

    This series has been an invaluable help, and can’t wait for the next installment covering, if I’m remembering right, using stores and models w/in an MVC architecture.

  8. Great work CAM! and thank you for share.

  9. CAM
    Thanks for the tutorial it’s fantastic.
    My opening page is a list page – I am trying implement the tabBar MVC on the detail page.
    However, I noticed if I have route: defined – the first button does not display as active – however if I click on it it activates.

    If the route is not defined then the first button lights up as active.

    Is this a bug? I am thinking perhaps I can activate it in my controller
    Thanks in advance
    JRS

    • Can you give more details, maybe post some js code or upload an example with the problem you’ve found so I could have a look to understand better the issue.

      • Hi CAM,
        Sorry for the delayed response – but I stumbled through putting together some code. I added to your example using Sencha list example from Vimeo. I am still struggling with Views and Viewports – so it took me quite a while to get something working. Hopefully, I can illustrate the problem. So here are the changes I made to your tutorial

        To the viewport.js file

        App.views.Viewport = Ext.extend(Ext.Panel, {
            fullscreen: true,
            layout: 'card',
            cardSwitchAnimation: 'slide',
            dockedItems: [
                {xtype: 'toolbar',
                title:'hello'}
            ],
            items: [{xtype: 'HomeList'},
                    {xtype: 'HomeDetail'}
            ]
        });
        App.views.HomeDetail = Ext.extend(Ext.Panel, {
            fullscreen: true,
            layout: 'card',
            cardSwitchAnimation: 'slide',
        	dockedItems: [
        	    {
        	    	xtype: 'TabBarMvc',
        	    	items: [
        		        {
        		        	text: 'Home',
        		        	iconCls: 'home',
        		        	route: 'Home/index', // custom property of the TabBarMvc component
        		        },
        		        {
        		        	text: 'About',
        		        	iconCls: 'info',
        		        	route: 'Home/about', // custom property of the TabBarMvc component
        		        },
        	        ],
        	    },
        	],
        });
        Ext.reg("HomeDetail", App.views.HomeDetail);
        
        // End of viewport.js
        

        Created a file clist.js for list and store also view (also have problems dealing with scope)

        // Clist.js (most of this code is from Sencha list example)
        Ext.regModel('Contact', {
            fields: ['firstName', 'lastName']
        });
        
        var store = new Ext.data.JsonStore({
            model  : 'Contact',
            sorters: 'lastName',
        
            getGroupString : function(record) {
                return record.get('lastName')[0];
            },
        
            data: [
                {firstName: 'Tommy',   lastName: 'Maintz'},
                {firstName: 'Rob',     lastName: 'Dougan'},
                {firstName: 'Ed',      lastName: 'Spencer'},
                {firstName: 'Jamie',   lastName: 'Avins'},
                {firstName: 'Aaron',   lastName: 'Conran'},
                {firstName: 'Dave',    lastName: 'Kaneda'},
                {firstName: 'Michael', lastName: 'Mullany'},
                {firstName: 'Abraham', lastName: 'Elias'},
                {firstName: 'Jay',     lastName: 'Robinson'}
            ]
        });
        
            var list = new Ext.List({
                layout: 'fit',
                itemTpl : '{firstName} {lastName}',
                grouped : true,
                indexBar: true,
                onItemDisclosure: true,
                store: store,        
            });
                list.on('disclose', function(record, index, evt){
                console.log('disclose pressed');  
                Ext.dispatch({
                    controller: 'Home',
                    action: 'detail'
                });
            });
        
         App.views.HomeList = Ext.extend(Ext.Panel, {
            items:     
            [
                list
            ],
        });
        Ext.reg('HomeList', App.views.HomeList);
        
        //end of clist.js
        

        Added 2 actions to the HomeController.js file

            // list
            clist: function()
            {
                console.log('HomeController.js(6): list');
                if(!this.HomeIndexView)
                {
                    this.HomeIndexView = this.render({
                        xtype: 'HomeList'
                    })
                }
                this.application.viewport.setActiveItem(this.HomeIndexView);
        
            },
            // Detail action
            detail: function(options)
            {
                console.log('HomeController.js(19): detail action');
                if ( ! this.indexView)
                {
                    this.indexView = this.render({
                        xtype: 'HomeDetail',
                    });
                }
                this.application.viewport.setActiveItem(this.indexView);
            },
        
        // end of HomeController.js
        

        App.js was modified to show the list as opening screen

        //App.js
        Ext.regApplication({
            name: 'App',
            defaultUrl: 'Home/clist',
            
            launch: function()
            {
               this.viewport = new App.views.Viewport();
            },
        });
        

        Again I apologise for the bad code – but I am just trying get something working to illustrate the issue.
        When starting the App the list of names should appear.
        Click on the disclosure icon it should display your demo page – but you will notice tabBar button home is not active – I believe it should be active.

        Also there could be problems with the way I am trying to implement.
        Thanks again for all your great tutorials – very helpful.
        Best Wishes
        JRS

        • Hi JRS.

          TabBarMvc was not working properly when historyUrl property was not passed to the dispatch function, but this issue is fixed now.

          A button will display as active only when its route will be the same as the one dispatched.

          In your example you have the first button route set to Home/index, but the dispatched one is Home/detail, so there will be no active button.

  10. Keep up the good work!

Add a Comment

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Also, if you want to display source code you can enclose it between [html] and [/html], [js] and [/js], [php] and [/php] etc