Showing posts with label Sencha. Show all posts
Showing posts with label Sencha. Show all posts

Wednesday, December 7, 2016

Top 3 Issues We Face With Sencha Touch Android Native App

As we all know using Sencha Touch and Cordova/Phonegap we can build native android application. Here in this blog I will mentioned top 3 issues we have to face while using Sencha Touch with Cordova and build native application. All three issues are related to Android Back Button event.



1) Android Back Button Press



Most of the android users are very much used to with android phone back button and Sencha Touch framework is very much user friendly with iOS. With Sencha Touch app on android, if you press back button of phone it exits the app instead of going back to previous screen. Because this back event is not properly handled by Sencha Touch framework. To solve this you have to bind Android back key press event with cordova and add your logic to go back to previous screen. I have mentioned this in my blog.

Please check Cordova Android Back Button Event

2) Hide Sencha Touch Pickers on Back Key Press



Ae we know sencha touch select field, date picker, action sheet shows picker to choose items from. Normally in android this types of pickers are dismissed when user press android back button. Button in case of sencha touch this types of pickers are nothing but floating panels and that does not hide on back key event. To solve this you have to bind Android back key press event with cordova and add your logic to go back to previous screen. I have mentioned this in my blog.

Please check Sencha Touch Hide Pickers On Android Back Key Press

3) Hide Sencha Touch Alert Box on Back Key Press



In Sencha Touch we have alert and confirm boxes. Normally in android this types of dialogs are dismissed when user press android back button. Button in case of sencha touch this types of alerts are nothing but floating panels and that does not hide on back key event. To solve this you have to bind Android back key press event with cordova and add your logic to go back to previous screen. I have mentioned this in my blog.

Please check Sencha Touch Hide Alert Box on Android Back Key Press

Saturday, October 22, 2016

Add Google Place Auto Suggest to Sencha Touch

Hello,

Recently in one of my project we had a requirement to add Google Place Auto Suggest in Sencha Touch app and I faced certain issue in that so in this blog I am going to explain how to do this.

You can get more information about google place auto suggest from following link.

https://developers.google.com/places/web-service/autocomplete

When you implement it in any web app you will get following result.


As when you start typing it will give you suggestions and you can pick any one suggestion from it.

When we implement same thing with Sencha Touch text field it was working fine. When you start typing suggestions were working but the problem was when user tap to pick one of the suggestion it was not working. It just closes the suggestions and nothing is saved in textfield. There were some solutions like adding some classes and all. I tried everything but it was not working at all. So I came up with different solution. First of all add following textfield in your view.

{
xtype: 'panel',
flex:'1',
items:[
{
xtype: 'textfield',
placeHolder: 'TYPE IN THE CITY OR THE ADDRESS',
itemId: 'autoSuggest',
id: 'autoSuggest',
height: 10,
inputCls:'x-input-el x-form-field x-input-text grey-input',
name: 'address',
allowBlank: false,
autoCapitalize: false,
clearIcon: false
}
]
}


Now bind key up event for this textfield in your controller.

'#autoSuggest': {
keyup: 'onSearchAddressTap'
}

Now our logic is the query Google place API manually and store result in Data Store and show it in dataview. So we will need model and store.

Following is our model.

Ext.define('MYAPP.model.AddressSuggestion', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            { name: "description", type: 'string' }

        ]
    }
});

And Following is our store.


Ext.define('MYAPP.store.AddressSuggestion',{
    extend:'Ext.data.Store',
    config:{
        model: 'MYAPP.model.AddressSuggestion',
        autoLoad: true,
        proxy:{
            type: 'memory'
        }
    }
});

Now lets key up event.

onSearchAddressTap: function(textField){
Ext.getStore('AddressSuggestion').removeAll();
if(textField.getValue().length > 3){
this.getVenueAddress().show();
Ext.Ajax.request({
url : 'https://maps.googleapis.com/maps/api/place/autocomplete/json?input='+textField.getValue()+'&types=geocode&language=fr&key=YOURKEY',

scope : this,
//method to call when the request is successful
success:function(response,opts)
{
var result = Ext.decode(response.responseText);
for(var i =0;i
Ext.getStore('AddressSuggestion').add({'description':result.predictions[i].description});
}
console.log(result);
},

failure:function(err)
{

}
});
}
}

So as you can see above we are sending an Ajax request to google maps api and store result in Datastore. 

Now add following dataview in your view just below the above textfield.

{
xtype: 'panel',
flex: '1',
layout: 'fit',
items: [
{
xtype   : 'dataview',
margin: '0 20 0 20',
itemId: 'venueAddress',
id: 'venueAddress',
itemTpl:
[
'<table style="border-bottom: 1px solid #e3e3e3;"><tr><td style="padding-bottom: 12px;padding-top: 12px;width:95%"><div style="color:#262626;font-size: 15px">{description}</div></td><td><img width="40" height="40" src="resources/images/howMuchSpace.png" /></td></td></tr></table>'
],
store: 'AddressSuggestion'
}

]
}

So now as soon as you start typing you will get result filled up in dataview and then add itemtap event of dataview and get the selected address and hide the dataview.

onVenueAddressItemTap: function(list, index, target, record, e){
this.getAutoSuggest().setValue(record.get('description'));
this.getVenueAddress().hide();
}

Ultimate output is like following.



Hope this helps you.

Saturday, October 15, 2016

Sencha Touch Hide Pickers on Android Back Key Press

Hello,

I recently published an article about hiding sencha touch Message box on press of back button in android. You can read it here Sencha Touch Hide Alert Box on Android Back Button Press

This post is something similar to it. Recently we created an android application with Sencha Touch and Cordova for client. After testing, client came up with requirement that if any picker is open for example calendar picker or select field picker etc. It should be closed on press of back button in android. So after efforts of half an hour, I found a solution. 

In Sencha Touch pickers are basically floating panels. So what we have to do it get all the floating panels and see if it's hidden or not. If not hidden then hide it.

First of all you have to bind back button event of android using cordova. Please check this cordova document on how to do this here.

Once you add backbutton listener, add following code.

var floatingComponents = Ext.query('.x-floating');
for(var i=0;i
var component = Ext.getCmp(floatingComponents[i].id);
if(component){
if(component.isHidden() == false){
component.hide();
return;
}
}
}

As you can see in above code, first we are finding all the floating component using Ext.query and class x-flaoting

Then we loop through it and find component using id of floating component and check if component is not hidden then hide it and return from there. 

Hope this helps you.

Sunday, September 25, 2016

Sencha Touch Hide Alert Box on Android Back Button Press

Hello,

Recently in one my project our client gave use very strange requirement. We have used Sencha Touch to create application and used Cordova to create native app.

As we all know with Sencha Touch we use Ext.Msg.alert() to show user alert. This alert has OK button. When user tap on that alert goes away.

However in our case client asked us to hide this alert if its on and user presses virtual or physical hardware button of android phone. So after hearing this requirement first of I was confused and was not sure how to implement it. But after looking at docs and source code of Ext.Msg class solution was very easy so on this blog I am going to explain how to do this.

First of all we have to bind back button key event. Add following code to your app.js file.

if (Ext.os.is('Android')) {
            document.addEventListener("backbutton", Ext.bind(onBackKeyDown, this), false);
            function onBackKeyDown(e) {
            }
}

Now as we all know Ext.Msg is singleton class and the alerts and confirm boxes are nothing but a floating panels. So we can just simply check if it's hidden or not and hide it if required. Check the following code.


if (Ext.os.is('Android')) {
            document.addEventListener("backbutton", Ext.bind(onBackKeyDown, this), false);
            function onBackKeyDown(e) {
                     if(Ext.Msg.isHidden() !=  null){
                             if(Ext.Msg.isHidden() == false){
                                       Ext.Msg.hide();
                             }
                     }
                     else{
                             var comp = Ext.getCmp("ext-sheet-1");
                             if(comp){
                                 comp.hide();
                            }
                     }
                     e.preventDefault();
            }
}


As you can see in above code. First we are checking Ext.Msg.isHidden() !=  null, this is to check if there is no instance of alert is created yet, there is no meaning of hiding it.

Then we check if Ext.Msg.isHidden() == false then just hide it or else don't do anything. That's it. Hope this helps you.

Saturday, September 3, 2016

PDF.JS Not Working - Sencha Touch 2 PDF Panel Not Working After Build

Hello,

Recently in one of my project we were using following Sencha Touch 2 PDF panel to display PDF in native application build with Cordova.

I faced a strange issue here. App was working fine without any build but when I generate a production build and build the native android app PDF panel stopped working and throws following error.

Unhandled rejection: TypeError: undefined is not an object (evaluating 'viewBox')

I tried to find out a solution for sometime but could not get any. I was using following PDF panel.

https://github.com/SunboX/st2_pdf_panel

Suddenly I saw that there is no update since last three years and that could be probably be the reason. So what I did is downloaded latest PDF.js and built the minified files and used it and bingo it worked. So here I am going to explain the steps.

1) First of clone latest PDF.js source from it's git.

$ git clone git://github.com/mozilla/pdf.js.git

2) Now go to the folder.

$ cd pdf.js

3) Install Node.js if its not installed.

$ npm install -g gulp-cli

4) After node.js is installed, install all the dependencies.

$ npm install

5) Build PDF.js files.

$ gulp generic

it will generate minified files in directory build/generic/build/

Copy pdf.js and pdf.worker.js files from this directory to your lib folder and that's it now PDF will be displayed even in testing or production build.

Monday, June 20, 2016

Sencha Touch Data Store Tips and Tricks

Recently one my friend asked me for some help on Sencha Touch Data store and form our conversation I got an idea for this blog.

Tip 1

If you want to pass extra params to store there are two ways to do that.

1) Pass it in load method.

store.load({
            params: {
                param1 : value1
            }
        });

2) Set it as extra param.

store.getProxy().setExtraParams({
            'param1' : value1,
            'param2': value2
        });

So what's the difference between this two and when to use which.

When you have requirement to add and use params only once. Also every time store load, you want to pass different params you have to use below method.

store.load({
            params: {
                param1 : value1
            }
        });

Params added here are only added once. Next time when you call load method of store, these params are not passed. You can use it for dynamic parameters.

When you have fixed params that you have to pass every time,  a store is loaded. You have to use following method.

store.getProxy().setExtraParams({
            'param1' : value1,
            'param2': value2
        });


This will permanently add those params to the store and it will be passed every time store.load() method is called.


Tip 2

How can you add callback function for store load dynamically. Again there are two ways to do that. If you want to add and use it only once, use following method. 

store.load({
       callback: function(){
              console.log('store is loaded'); 
       }
});

But if you want to wait for store load every time you should use following logic.

store.on({
       load: function(){
              console.log('store is loaded'); 
       },
       scope: this
});

Tricks

1) To reset all the extra params in store use do the following.

store.getProxy().setExtraParams({
});

Just pass the empty object in function and it will clear all previous params.

2) Reset page param in store when using list paging.

You can use currentPage config. store.currentPage = 1

3) Get raw response from of store load.

store.getProxy().getReader().rawData

This will return you all the raw data your API has returned.

Saturday, June 18, 2016

Sencha Touch Create Dropdown Like Standard HTML Control

Recently in one of my project, we have a requirement to create dropdown like standard HTML dropdown with Sencha Touch. See the below screenshot.


As we know in Sencha Touch Selectfield (dropdown)  uses either floating panels and bottom picker to show and choose value from one. So in this blog I am going to mention how to do this. 

First of all following should be our views.

{
    xtype : 'panel',
    height: 40,
    itemId: 'monthSelector',
    id: 'monthSelector',
    style: 'background-color:#ffffff;color: #019297;border-bottom:1px solid #019297;font-size: 12px',
    html: '<div style="height:100%;width:100%;display: table;">$lt;div style="display: table-cell;vertical-align: middle;">Select Month</div><img style="position:relative;float:right;top:10px" height="20" width="20" src="resources/css/images/black-down-arrow.png"/></div>',
    listeners: {
         initialize: function( element ) {
               this.element.on({
                    tap: function( ele ){
                             MyApp.app.getController('MyController').toggleDropDown();
                    }
               });
         }
     }
},

{
xtype: 'dataview',
itemId: 'expenseMonthSelector',
id: 'expenseMonthSelector',
scrollable: false,
style: 'font-size: 12px',
height: 80,
store: {
fields: ['id', 'name','color'],
data: [
{id : 1, name: 'Current Month', color: '#9decf0'},
{id : 2, name: 'Previous Month', color: '#6cd2d6'}
]
},
itemTpl: '<div style="background-color: {color}; color:#000000;height:40px;width:100%;display: table;"><div style="display: table-cell;vertical-align: middle;padding-left: 20px">{name}</div></div>'
}

As you can see above we have one panel with layout look like a dropdown and one dataview which will act like options for the dropdown.

After this we will add our toggleDropDown function to toggle dropdown.

toggleDropDown: function(){
        if(this.getExpenseMonthSelector().isHidden() == true){
            this.getExpenseMonthSelector().show();
        }else{
            this.getExpenseMonthSelector().hide();
        }
},

Above code will simply show hide data view. Hope this helps you.

Thursday, May 5, 2016

Create Dynamic Half Circular Progress Bar With HTML 5 CSS 3 - Create Gauge Chart In HTML 5

Recently in one of my project there was a requirement to create half circle gauge chart to show progress of some actions. Something like below image


So first of all I decided to use some charts library to create UI like this but since this was mobile application I do not find any suitable chart library. So I decided to build UI from scratch. So here is the step by step guide. 

First our basic HTML and CSS

<div style="visibility: hidden" class="container" id="container">
                       <div id="border" class="active-border">
                            <div id="circle" class="circle">
                               <div id="needle" class="needle"></div> 
                            </div>
                        </div>
                      </div>

Now the basic CSS.

.container{
    width: 100%;
    height: 100%;
    overflow: hidden;
    position: relative;
    top: -25%;
}

.circle{
    position: relative;
    top: 5px;
    left: 5px;
    text-align: center;
    width: 200px;
    height: 200px;
    border-radius: 100%;
    background-color: rgba(0,0,0,1);
}

.border{
    position: relative;
    text-align: center;
    width: 210px;
    height: 210px;
    border-radius: 100%;
    top : 50%;
    background-color:#00a651;
}

.needle {
    position: absolute;
    background-color: #f0566f;
    height: 100%;
    width: 0.5%;
    left: 50%;
}

.needle:before {
    content: "";
    position: absolute;
    top: 0px;
    left: -10px;
    width: 0px;
    height: 0px;
    border-top: 10px solid transparent;
    border-right: 20px solid #f0566f;
    border-bottom: 10px solid transparent;
    -webkit-transform: rotate(90deg);
}

Now here comes the dynamic things. first of we have to set height and width of all the elements inside container to screen width and height.

var containerWidth = 0;

if(document.getElementById('container').clientHeight > document.getElementById('container').clientWidth){
containerWidth = document.getElementById('container').clientWidth;
}
else{
containerWidth = document.getElementById('container').clientHeight;
}

var circleWidth =  containerWidth - 10;
var activeBorderWidth =  containerWidth;
var padding = (document.getElementById('container').clientWidth - document.getElementById('container').clientHeight) / 2;

document.getElementById('circle').style.width = circleWidth + 'px';
document.getElementById('circle').style.height = circleWidth + 'px';
document.getElementById('container').style.paddingLeft = padding + 'px';

document.getElementById('border').style.width = activeBorderWidth + 'px';
document.getElementById('border').style.height = activeBorderWidth + 'px';

So as you can see we are setting border with to container width and make it fit inside container and setting inner circle width to bit less than border width to create circular progress bar. Now we calculate transform degrees to set up liner gradient to show progress. 

var degree = (180 * Number(count)) / (Number(total));

document.getElementById('border').style.background = '-webkit-linear-gradient('+degree+'deg, #9a9a9a 50%, #00a651 50%)';

As you can see since we have half circle we are calculating degrees by 180. If you want full circle progress bar then calculate it with 360 degree.                 

Now set needle transformation.

var needleTransformDegree = 180 - degree;

document.getElementById('needle').style.webkitTransform = 'rotate('+needleTransformDegree+'deg)';
               
document.getElementById('container').style.visibility = 'visible'

That's it and end result look something like below.


Hope this helps you.

Saturday, March 26, 2016

Sencha Touch 2.2 Scrolling Issue in Android 5.1

Hello,

If your application is created with Sencha Touch 2.2 it must be having scroll issue for forms, list and carousels in android 5.1 on words.

The reason is some chrome updates have broken Sencha Touch scrolling logic and that's why it's not working so what is the solution, well the simple solution is to update your sencha touch version in the app. But what if the app is not created with Sencha CMD. Yes in my case previous developer have created app with loading sencha-touch-all,js and css. So I can not simply modify it with new version y sencha cmd and there were lots of CSS overrides. So what to do. In this blog I am going to explain.

First of all create folder with name util in your app folder. Now create new JS file in util folder and name it "PaintMonitor.js" and add following code to it.

/**
 * Created by hirendave on 3/7/16.
 */
Ext.define('SEOshop.util.PaintMonitor', {
    override: 'Ext.util.PaintMonitor',

    uses: [
        'Ext.env.Browser',
        'Ext.env.OS',
        'Ext.util.paintmonitor.CssAnimation',
        'Ext.util.paintmonitor.OverflowChange'
    ],

    constructor: function(config) {
        return new Ext.util.paintmonitor.CssAnimation(config);
    }

}, function () {
    //
    console.info("Ext.util.PaintMonitor temp. fix is active");
    //
});

Crete one more file with name "SizeMonitor.js" and add following code to it.

/**
 * Created by hirendave on 3/7/16.
 */
Ext.define('SEOshop.util.SizeMonitor', {
    override: 'Ext.util.SizeMonitor',

    uses: [
        'Ext.env.Browser',
        'Ext.util.sizemonitor.Default',
        'Ext.util.sizemonitor.Scroll',
        'Ext.util.sizemonitor.OverflowChange'
    ],
    constructor: function (config) {
        var namespace = Ext.util.sizemonitor;

        if (Ext.browser.is.Firefox) {
            return new namespace.OverflowChange(config);
        } else if (Ext.browser.is.WebKit) {
            if (!Ext.browser.is.Silk && Ext.browser.engineVersion.gtEq('535') && !Ext.browser.engineVersion.ltEq('537.36')) {
                return new namespace.OverflowChange(config);
            } else {
                return new namespace.Scroll(config);
            }
        } else if (Ext.browser.is.IE11) {
            return new namespace.Scroll(config);
        } else {
            return new namespace.Scroll(config);
        }
    }
}, function () {
    //
    console.info("Ext.util.SizeMonitor temp. fix is active");
    //
});

Now go to your app.js file and add following code to your application object.

requires: [
        'YOUR_APP.util.SizeMonitor',
        'YOUR_APP.util.PaintMonitor'
    ]

That's it and it should solve all the scrolling and rendering issue in Android 5.1 and above version.

Hope this helps you.

Sunday, May 3, 2015

Sencha Touch Add Loading Mask on Each Ajax Request

Hello,

This is short and quick blog about how to add global logic to show and hide load mask for each Aajx request in Sencha Touch Application.

In Sencha Touch application Ajax request is used in two ways. Either one can call Ajax request with

Ext.Ajax.request({
})

Or in stores you may have Ajax proxy which will generate Ajax request when store is auto loaded or you call load method. Since this is background process you may want to show loading mask to users. For that you can call setMasked method before Ajax request and you remove that mask in success and failure function. This you have to write everywhere in your code. So it's better to add it to single place.

This how you can do it. Add following code to your launch function in your app.js file.

                Ext.Ajax.on("beforerequest", function(){
Ext.Viewport.setMasked(true);
});

Ext.Ajax.on("requestcomplete", function(){
Ext.Viewport.setMasked(false);
});

Ext.Ajax.on("requestexception(", function(){
Ext.Viewport.setMasked(false);
});

Since Ext.Ajax is singleton class we have added event handler for beforerequest, requestcomplete and requestexception.

In beforerequest event handler we are setting mask to viewport and in requestcomplete and requestexception handler we are removing it. That's it and the mask will be displayed every time there is a ajax request.  Hope this helps you. 

Friday, April 10, 2015

Sencha Touch MessageBox Can Not Be Closed.

Recently in one of my Sencha Touch project, I had an issue with MessageBox, specifically in Android. The issue was when user taps on Ok button message box does not go away. Ideally it should hide, but instead of that it's body is still visible and due to that app was not usable. Issue was hide animation of message box. ActiveAnimation is blocking messagebox from closing properly, so the workaround is to force "end" function of that animation. it was not ended correctly, probably because of events.

Add following code to your application.

 Ext.override(Ext.MessageBox, {    
            hide:  function() {
                if (this.activeAnimation && this.activeAnimation._onEnd) {
                    this.activeAnimation._onEnd();
                }
                return this.callParent(arguments);
            }
});

This will solve your problem. Now message box will be hidden as soon as you tap on Ok button. Hope this helps you.

Monday, September 1, 2014

Sencha Touch - One Way to Implement Autosuggest Textbox

Hello,

Recently in one of our Sencha Touch project there was a requirement to add autosuggest textbox. As user starts typing there should be some suggestions and user should be able to either select one of the option or type in the value. In this blog I am going to explain the approach I used for that. Please note that there could be other ways to implement this.

First lets create a store that has the options for auto suggestions. For that here is the model definition.

Ext.define('MyApp.model.Option', {
    extend:'Ext.data.Model',
    config:{
        fields:[
            { name:'id', type:'int' },
            { name:'text', type:'string' },
        ],
        idProperty: 'id'
    }
});

Now lets create a store which holds the data.

Ext.define('MyApp.store.Option', {
  extend: 'Ext.data.Store',
  config: {
    model: 'MyApp.model.Option',
    autoLoad: true,
    proxy: {
      type: 'memory'
    },
    data : [
        {id: 1,    text: "Option 1"},
        {id: 2, text: "Option 2"},
        {id: 3, text: "Option 3"},
        {id: 4, text: "Option 4"}
    ]
  }
});

Now our approach is quite simple we will create a floating panel with list and assign this store to it and as soon as user starts typing in textbox, we will show this panel near to textbox and filter store with what is typed in textbox. So lets create a floating panel.

Ext.define('MyApp.view.Traffic.AutoSuggestPanel', {
extend: 'Ext.Panel',
xtype: 'autosuggestpanel',
config: {
modal: true,
hideOnMaskTap: true,
hidden: true,
height: '180px',
width: '94%',
layout: 'fit',
margin: '-9px 0 0 0',
items: [
{
xtype: 'list',
id: 'autoSuggestList',
itemId: 'autoSuggestList',
itemHeight: 30,
itemTpl: '<div style="font-size: 13px">{name}</div>',
store: 'Option'
}
]
}
});

We will add this panel to viewport when app launches, as you see it's hidden initially so it will not be visible.

Ext.Viewport.add(Ext.create('MyApp.view.Traffic.AutoSuggestPanel'));

Now lets add keyup event to textfield on which we want to show this suggestions. Also we will add our floating panel as reference to controller.

 config: {
        refs: {
              autoSuggestPanel: 'autosuggestpanel'
              autoSuggestList: '#autoSuggestList',
              autoSuggestTextField: '#autoSuggestTextField'
       }
}

autoSuggestTextField: {
            keyup: 'onAutoSuggestTextFieldKeyUp'
            },
autoSuggestList: {
            itemtap: 'onAutoSuggestListItemTap'
            },

And define it's handler in controller.

onAutoSuggestTextFieldKeyUp: function(text){
        if( this.getAutoSuggestPanel().isHidden( ) ) {
            this. getAutoSuggestPanel().showBy(text,'bc-tc' );
        }

        Ext.getStore('Option').clearFilter();
        Ext.getStore('Option').filter('text',text.getValue());
}


Above code will show auto suggest list next to textfield. Now we have to add itemtap event for the list and set it's value in textbox. Here is handler for it.

onAutoSuggestListItemTap: function(list, index, target, record){
        var name = record.get('text');
this. getAutoSuggestPanel().hide();

       this.getAutoSuggestTextField().setValue(name);
}

That's it and you have autosuggest textbox ready.

Saturday, May 31, 2014

Sencha Touch Create Navigation Drawer (Slide Navigation Like Gmail)

Recently in one of our sencha touch project we have created Navigation Drawer for sencha touch. In this blog I will explain how to create it.

Let's first understand what is navigation drawer. We are all familiar with Facebook slide navigation. It has button on top left corner of toolbar, when you tap on it, the content in center slide to right and menu opens with left to right animation. When you again tap on that menu is closed with right to left navigation and center content slides to left. Navigation drawer is introduced in Android 4.0. It's  slight variation of slide menu. Here the center content does not slide left or right but the menu comes on top of slide content. However the top toolbar is still accessible so user can still close the menu. Now lets see how to create this in sencha touch. Please note that this is the one way we used to create navigation drawer, there could be other options as well.

First lets create a list which will act as navigation menu.

Ext.define('SlideNav.view.Navigation', {
    extend: 'Ext.List',
    xtype: 'navigation',
    modal: true,
    hideOnMaskTap: false,
    requires : ['Ext.data.Store'],
    config: {
        width: 250,
        itemTpl : '{title}',
        data : [
            {
                title : 'Item 1'
            },
            {
                title : 'Item 2'
            },
            {
                title : 'Item 3'
            }
        ]
    }
});

As you can see in above code we created a list and set it as modal element with modal config. List extends Ext.container so we can open it as modal element. This is our navigation menu. Now lets add a button on top left corner of our toolbar and create the main view.

Ext.define('SlideNav.view.Main', {
    extend: 'Ext.Container',
    xtype: 'main',
    config: {
        style: {
            zIndex: -1,
            position: 'absolute'
        },
        layout:{
            type: 'card',
            align: 'stretch'
        },
        items: [
            {
                xtype: 'toolbar',
                docked: 'top',
                title: 'Slide Navigation',
                items: [
                    {
                        xtype: 'button',
                        iconCls: 'list',
                        ui: 'plain',
                        itemId: 'slideNavBtn',
                        id: 'slideNavBtn'
                    }
                ]
            },
            {
                xtype: 'panel',
                itemId: 'slideContainer',
                layout: 'card',
                id: 'slideContainer',
                items: [
                    {
                        xtype: 'panel',
                        html: 'Hello Welcome to The Design Shop.Sencha Touch is very good framework.'
                    }
                ]
            }
        ]
    }
});

So this is our main view and it has toolbar with top navigation button. Now lets add tap event for it in controller and add logic to open and close the menu.

Ext.define('SlideNav.controller.App',{
    extend: 'Ext.app.Controller',
    config:{
        refs:{
            main : 'main',
            navigation : 'navigation',
            navBtn : '#slideNavBtn'
        },
        control : {
            navBtn : {
                tap : 'toggleNav'
            }
        }
    },
    init: function() {
        this.toggle = 0;
    },
    toggleNav : function(){
        var me = this;
        if(!me.getNavigation()) {
            Ext.create('SlideNav.view.Navigation');
            Ext.Viewport.add(me.getNavigation());
            me.getNavigation().show();
        }
        if(this.toggle == 0) {
            Ext.Animator.run({
                        element: me.getNavigation().element,
                        duration: 500,
                        easing: 'ease-in',
                        preserveEndState: true,
                        from: {
                            left: -250
                        },
                        to: {
                           left: 0
                        }
        });
        this.toggle = 1;
        }
        else {
        Ext.Animator.run({
                            element: me.getNavigation().element,
duration: 500,
easing: 'ease-in',
preserveEndState: true,
from: {
left: 0
},
to: {
left: -250
}
});
            this.toggle = 0;
        }
    }
});

As you can see in above code we are using one controller variable toggle to know the state of menu and and on tap of it first we create the navigation and menu and add it to viewport and then we are using Ext.Animator to open it with animation. So basically we are setting left property from -250 to 0 as 250 is the width of the menu. You can change it according to your requirement or set dynamic width here.

Friday, May 23, 2014

Make Sencha Touch Site With Routes SEO Friendly

Recently in one of our project which was a sencha touch app we faced an issue with SEO. As far as I know a site should have all the unique URLs for better SEO. With unique url Google crawler can crawl your URLs more efficiently. Now this could be the issue with Sencha Touch app as we know. We open sencha touch app with our domain URL and then we don't have any URL changes. All our views are loaded locally. So our url will stay like http://mydomain.com/app/index.html.

Now this is not good for SEO as your URL is not changing so google can not index your site. Now this is not good if you are selling your products on your site as normally people search with product name and they can not find your site urls in Google. So what to do? To resolve this issue sencha has introduced routes and history support. So what it does is it changes your URL as and when you navigate through sencha app. Something like

http://mydomain.com/app/index.html#productlist/cat1name
http://mydomain.com/app/index.html#productdetail/product-name

So now you have all the unique urls on your site so those urls can be indexed by Google and can be displayed in search result. But wait we have another problem here. Google ignores all the content of the url after hash tag. So after using routes we are back to the same problem again.  Still our sencha touch app is not SEO friendly. So what to do now. Google suggest to use Hash Bangs(#!) instead of Hash tag (#) So if use this our routes will not work as it works only on has tag so what to do? We have to modify our sencha touch routes logic to work with Hash Bangs (#!) For that if you are manually adding history as follow, you have to override add history function.

MyAppName.app.getHistory().add(new Ext.app.Action({
            'key': 'value'
}), true);

It will convert your url as follow.

http://mydomain.com/app/index.html#key/value

You have manually insert exclamation mark (!) here.

MyAppName.app.getHistory().add(new Ext.app.Action({
            '!key': 'value'
}), true);

Now your url looks like below.

http://mydomain.com/app/index.html#!key/value

So it's a Hash Bangs and now this URL can be indexed by Google. But that will break your routes logic as now your key would be !key. To make it working you have to change your root definition and add you have to prefix it with . See the example below.

routes: {
            '! productlist/: text': 'goToProductList',
            '! productdetail/:text' : 'goToProductDetail'
}

That's it and your routes will work as usual. So with this trick a developer is happy, a customer is happy and a SEO guy is happy.

Saturday, April 19, 2014

Add Tap Hold Event in Sencha Touch panel

Recently in one of my project, requirement was to display context menu when user tap and hold on sencha touch panel. We all know we can add itemtphold event on sencha touch list. But in my case I have to use panel. In this blog I will explain how to add tap hold event to sencha touch list.

The logic is simple , first of all we have to bind touchstart and touchend event on panel. When touch starts we will check after some interval if touchend event is fired or not. If there is no touch event fired that means user is still holding tap. See the code below.

{
      xtype: 'panel',
      html: 'tap and hold',
     listeners: {
                            painted: function(element){
                            var isTouch = false;
                            element.on({
                            touchstart: function(){
                            isTouch = true;
                            setTimeout(function(){
                            if(isTouch == true){
                                 //There is a tap hold
                            }
                            }, 2000);
                            },
                            touchend: function(){
                            isTouch = false;
                            }
                            });
                            }
                            }
}

As you can see in above code after touch start event we are setting timeout to check isTouch variable after two seconds. You can change the interval according to your need. If touch ends before two seconds we set isTouch to false. So after timeout when this variable is checked you will get false and tap hold event is not fired.  This way you can add tap hold event to any of the sencha touch component.



Monday, March 10, 2014

Sencha Touch List Find Top Visible Item Index

Recently in one of my project we have a Sencha Touch list and top banner. Each list item have certain banner text to show. When user scrolls up or down we have to show the banner text of list item which is on top position in the top banner panel. It should change as user moves up or down.  For that we have to find top visible item index or record of list. So here is my logic for that. 

As we know that all the list item have certain fix height that we can specify with itemHeight config. Each list is scrollable with scroll view. We can simply calculate top visible item by dividing y offset of scrollview with item height of the list. This will give us top visible item index. Here is the logic to do that. Add reference of your list to controller and bind initialize event of the list.

myList:{
                initialize: 'onMyListInit'
 },

No we will bind scroll event to this list scroller.

onMyListInit: function(list){
          var listItemHeight = 53;
          var scroller = list.getScrollable().getScroller();
          scroller.on({
            scroll: function(scroller, x, y, e){
                          var currentVisibleItemOnTopIndex = parseInt(y/listItemHeight);
                }
          });
}

This logic will give us top visible item index. You can find record from index using getAt method of list store. In my case I get the record and use it's banner text to display on top banner above the list. It keeps changing as you scroll up or down the list. Please note that this trick will only work if you have simple list with fixed height of the item. If you have variable heights or group list.  This trick will not work. As in group list we have items arranged in groups so their indexes changed. We might have to find some other solution for that. If you have any idea post a comment.

Friday, February 28, 2014

Sencha Touch List Accordion Layout (Expand and Collapse Sencha Touch List Item)

Hello,

Recently I was working on a project where we have a requirement to have accordion layout in Sencha Touch List items. Basically there are few items in list and user should be able to expand and collapse it by taping in respective list item. In this blog, we will see how to implement it.  First set fix item height for the list. For example,

{
       xtype: 'list',
       itemId: 'itemsList',
       id: 'itemsList',
       store: 'Items',
       itemHeight: 30,
       itemTpl:'<div style="overflow:hidden">Touch me to Expand Item {itemName}'+
                           '<div>'+
                                  '{itemDescription}'
                           '</div>'+
                    '</div>'
}

As you see in above code we have set height of item to 30 and set overflow to hidden so now you can have big texts as item description that will not be visible completely in collapsed mode. Now we have to add tap event for list in controller.

control: {
        '#itemsList': {
        itemtap: 'expandCollapseItemView'
        }
        }
And the function expandCollapseItemView in your controller.

expandCollapseItemView: function(list, index, target, record, event){
        var fromHeight = 0;
    var toHeight = 0;
    if(target.element.dom.clientHeight <= 30){
    fromHeight = 30;
    toHeight = 100;
     }else{
    fromHeight = 100;
    toHeight = 30;
     }
    Ext.Animator.run({
element: target.element,
duration: 500,
easing: 'ease-in',
preserveEndState: true,
from: {
height: fromHeight
},
to: {
height: toHeight
}
});
}

So if you check the above code we are identifying if we have expand or collapse based on client height of the item with the property target.element.dom.clientHeight. If height is less then or equal to 30 which is our item height for the list, we will expand the list item. After expanding list item the height will be 100 next time we will get clientHeight as 100 so we will collapse it to 30.

And we are running the animation with Ext.Animator.run and setting height from small to big or big to small.

With this you have expandable and collapsible list items.

Wednesday, December 25, 2013

How to use hasMany association in Sencha Touch Model

Hello,

In this blog I will explain how to use "hasMany" associations in Sencha Touch models. First lets see what are the associations in Sencha Touch.  Normally in RDBMS we have primary key and foreign keys and using which we can reference data from other tables. For example students and course. Here one course can have many students and a student belongs to specific course. This types of associations we can have in Sencha Touch. Sencha Touch data package supports associations along the models. For this blog lets take an example of Products and Merchants. Here a merchant can have many products and a product belongs to a marchant. In this case your models definition would look like following.

Ext.define('MyApp.model.Merchant', {
    extend: 'Ext.data.Model',

    config: {
        fields: [
            { name: 'id',                   type: "int"},
            { name: 'name',                 type: "string"}
        ],
        associations: [{ type: 'hasMany', associatedModel: 'MyApp.model. Product', name: 'products' }]
    }

});

Ext.define('MyApp.model.Product', {
    extend: 'Ext.data.Model',

    config: {
        fields: [
            { name: 'id',                   type: "int"},
            { name: 'title',                type: "string"},
            { name: 'description',          type: "string"},
            { name: 'cost',           type: "float"}
        ],
        idProperty: 'id',
        associations: [{ type: 'belongsTo', model: 'MyApp.model.Merchant', name: 'merchant' }]
    }

});

Above is the definition of product and merchant models. Here we have used associations config to define it. Merchant has specified the association as hasMany and product model specified it with belongsTo . You can specify more then one associations on single model. If you see the definition of association, we have specified three config here, type, model and name. Type is the type of association, associateModel specifies the other model and name defines the key of field.  You can use getter and setter with this name or use this name in list or dataview tpl. Please note here you have to specify both hasMany and belongsTo association to each model .If you skip either, it will not work.  Now let's say we have to set tpl in the list for the products and show the merchant name there. You tpl should like like this.

var goodTpl = new Ext.XTemplate(
    '<div>',
        '<div>{title}</div>',
         '<div class="goodmerchant">By {merchant.name}</div>',
    '</div>'
);

As you see above we have used merchant.name to display name of the merchant for the product. It will fetch merchant name from merchant store using the association.

Adjust Sencha Touch Carousel Indicators

Hello,

Recently we have a requirement in one of our project, to move carousel indicators outside the carousels. See the image below.

Now the issue was, main div container for the indicators is using the inline styling to position indicators based on user preferences. So we can override with class as we have to change the logic of the inline style. For that you have to override base class of Sencha Touch, name of the class is Ext.carousel.Indicator, you will find this class in src/carousel/indicator.js file in your sencha touch source code. Find the following function in the class.

updateDirection: function(newDirection, oldDirection) {
        var baseCls = this.getBaseCls();

        this.element.replaceCls(oldDirection, newDirection, baseCls);

        if (newDirection === 'horizontal') {
            this.setBottom(0);
            this.setRight(null);
        }
        else {
            this.setRight(0);
            this.setBottom(null);
        }
    }

Here you can see it's setting bottom property to zero. We have to override this function. Create an override.js file and attach it to your index.html file after sencha touch js file and add following code to it.

Ext.override(Ext.carousel.Indicator, {
    updateDirection: function(newDirection, oldDirection) {
        var baseCls = this.getBaseCls();

        this.element.replaceCls(oldDirection, newDirection, baseCls);

        if (newDirection === 'horizontal') {
            this.setBottom(-20);
            this.setRight(null);
        }
        else {
            this.setRight(0);
            this.setBottom(null);
        }
    }
});

As you see in the above code, we set bottom property to -20. You can use your own number here depending on your designs.

Hope this helps you.