Category Archives: Sencha Touch 1

Formatting Credit Card Numbers – Dealing with Webkit Text Input Oddity

27 Apr 2012

Another day, another script added to my library…. I had a credit card text field in one of my Sencha Touch projects that needed to be “dash separated” while the person is typing. Below is my solution to this particular minor challenge. Please remove any other validation in your project as this already features:

  • Removal of all undesired alpha characters and symbols
  • Limited to a maximum of 16 numbers
  • Plays nice when you backspace from the end

Note that you shouldn’t try to correct a number in the middle of the string – if you delete a number the entire string gets re-written for the current string of numbers. Also, the first thing that may come to mind is the HTML 5 “pattern” attribute which accepts a regular expression with which to validate the text field value, however, that would validate as you type, not format the string in the desired manner while the typing is happening. Sencha has its own way of validating text fields which is essentially the same thing but the issue is that it also doesn’t do on-the-fly string formatting.

So here is the function itself, which works perfectly in a normal web-browser (yes, a caveat for android webkit follows):

...
    function do_ccFormat(str){
	var nums = str.substr(0,19).replace(/[^\d]/gi,'');
	var r = nums.match(/(\d){4}/g);
	if (r){	
	    var i=0, nStr = '';
	    for (;i

However, in Android 2.3.6's webkit there is some odd behavior that is a pattern that I'm sure others somewhere have come up against. So the pattern is this: first assume that you are doing some string manipulation on the keyup event, waiting for some criteria to be met at every key stroke before manipulating the string. In the case of my CC formatter its the 5th character that triggers the string re-write. So lets say you type 5 characters such as "12345". When "5" is typed the entire string is replaced with a new one that contains a new character/delimiter - like so: "1234-5". The very next character that you type will not go at the end where the cursor is but before the last character that was entered. So if we enter "6" the string ends up like this: "1234-65". Further, if you hit the backspace key you backup not from the cursor position (which is at the end of the string) but from where the last character was erroneously entered! Weird stuff! This gets worse with every new delimiter that is added to the string.

Upon inspection and some thought, I realize that replacing the value with a formatted value as you type is something that the Android 2.x webkit can't handle when it has to then figure out where the cursor should go. It seems that programatically setting the value does not update the cursor position for the field - it stays where it is. If you pay close attention when all this happens you'll notice that the cursor will quickly jump around.

I then hit upon the idea of blurring the field (thinking to therefore dump whatever erroneous machinations may be at play) and then quickly give focus back to it knowing that the act of giving focus to a field sets the cursor to the end. This works as long as you space out the blur/focus methods with a setTimeout. I spaced them out by 100 milliseconds hoping that the soft keyboard wouldn't flash. This appeared to work well Android 2.3.6 (a Samsung Galaxy S2). The keyboard would just sit there apparently none-the-wiser.

While this did seem to work it turned out to be unpredictable. In Android 2.2 the keyboard would go away and not reappear, sometimes that would happen in 2.3.6, and it does go away in Android 3.

Its unfortunate that this doesn't work in pre-ICS Android - it is what it is. Below is how I implemented it in Sencha Touch 1 - as you can see I apply it on keyup for iOS and ICS for as-you-type formatting. For pre-ICS Android I limit the keyed cc number length to a max of 16 characters and apply CC formater when the CC field is blurred (a length of 19 is needed otherwise since we are adding 3 additional characters in the form of the "-" delimiter).

I should end by saying that this obviously does not include a MOD10 check. I'll toss in my MOD10 checker at a later date as a separate article - it will include the ability to check not only entire cc numbers - which all of the readily available MOD10 checkers do - but it will also allow you to check a CC number for its **type** based on the first four digits. Until then, here's Credit Card String Formatter sample implementation:

...
items:[
  {
    xtype:'textfield',
    name:'cardNumber',
    inputType:'tel',
    maxLength:'19',//allow the 3 dashes needed to format the string to be included
    listeners:{
	keyup:function(o,e){
            if (Ext.is.iOS || (Ext.is.Android && Ext.is.AndroidVersion > 3)){
	        o.setValue(do_ccFormat(o.getValue()));
            } else { // assume old android
                o.setValue(o.setValue().substr(0,16));
            }
	},
        blur:function(o,e){
            if (Ext.is.Android && Ext.is.AndroidVersion < 3){
               o.setValue(o.setValue().substr(0,16));
               o.setValue(do_ccFormat(o.getValue()));
            }
        }
    }
  }
]
...

Sencha Touch: Adding/Removing Asterisk in Required Fields

08 Mar 2012

Another day, another thing learned. Today its those little asterisk’s that appear next to the labels of required fields in Sencha Touch 1. You can set them so that they appear when the component is instantiated via the “required” config option but there is no method to add or remove them after the fact. Also, if you are building your Sencha app the old non-mvc way that means that no components are not being destroyed/created as needed, so you can’t set the value for “required” to be an expression (i.e., something ? true : false) because if you do it will only work once – when the component is created.

Looking at the html that Sencha produces I see that a class is added to the component that is responsible for the appearance of the asterisk. Since Sencha does provide a method to add/remove classes the solution couldn’t be simpler:

...
// too add the asterisk to the form element refer to its div and:
Ext.getCmp('id_of_field').addCls('x-field-required');
// to remove the asterisk from the form element:
Ext.getCmp('id_of_field').removeCls('x-field-required');
...

Keep in mind that this is just a visual thing – no matter how you set the component’s “required” property there is no impact on any form validation.

Custom Message Boxes in Sencha Touch 1.1

08 Mar 2012

Sencha Touch comes with 3 modal message boxes: Alert, Confirm, and Prompt. In all cases the buttons are pretty hard-coded into them. There are many instances where specific button combination or wording is desired yet the defaults for the three canned Message Boxes fall short. I have seen various attempts at getting custom buttons and quite honestly they all seem convoluted to me. All I want is a straight-forward way of creating custom buttons within my Sencha Message Boxes.

IMO the easiest approach is to break away from Alert/Confirm/Prompt and create your own MessageBox via the Ext.MessageBox class.

First, lets declare a new Ext.MessageBox instance like so:

...
var msg = new Ext.MessageBox();
...

I’ve create a variable and assigned an empty Ext.MessageBox to it. What we need to do is to configure it and then show it, both of which can be accomplished with the Ext.MessageBox show() method which itself takes a configuration object which will populate our Message Box with everything that we need before it renders it to the screen. See this next bit of code for an example, which we’ll disect in a moment:

...
msg.show({
    title: 'Hello World',
    msg: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt',
    buttons: [{text:'Sure',itemId:'sure'},{text:'No Way',itemId:'noway'}]
});
...

The above will look like this:

Its easy enough, right? Note the “buttons” config option, this takes an array of configuration objects. Each object has “text” and “itemId” attributes. You can specify whatever you like as the values for these. As well, you can specify just one config object or more if you like. Please note that while there is a “width” config option for Ext.MessageBox setting it has no effect. As a result, the practical limit for custom buttons is 2. If you’re familiar with CSS, you could override the Sencha Touch styles being used on the buttons and squeeze in a third, space permitting.

The next step is to setup a callback function to handle user input. To do so simply assign a function to the fn: config option or do as I did and assign an anonymous function that is setup to receive the “response” from the user. The “response” is the “itemId” that you specified for the button.

...
function custombox(){
    var msg = new Ext.MessageBox();
        msg.show({
        title: 'Hello World',
        msg: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt',
        buttons: [{text:'Sure',itemId:'sure'},{text:'No Way',itemId:'noway'}],
        fn:function(response){
            console.log(response)
        }
    });
}
...

If the above is run and you tap on the “Sure” button you will see the string “sure” print to the console. Same thing for the “No Way” button – its itemId “noway” will also print to the console when its button is tapped. All you need to do is to insert some logic to handle either of the two responses.

Finally, to ensure that your MessageBox conforms to your text always execute a doComponentLayout() on your message boxes after showing them (see line 35 below).

A complete Sencha Touch Custom Message Box example:

...
Ext.setup({
    onReady: function(){
	
        var rootPanel = new Ext.form.FormPanel({
	        fullscreen: true,
	        items: [
		    {
		        xtype:'button',
		        text:'Show Custom Message Box',
		        scope:this,
		        handler: custombox
		    }
	        ],
	        dockedItems:[
		    {
		        xtype:'toolbar',
		        dock:'top',
		        title:'Message Box Example'
		    }          
	        ]
	    }
        );
	
	function custombox(){
	    var msg = new Ext.MessageBox();
	    msg.show({
		title: 'Hello World',
		msg: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt',
		buttons: [{text:'Sure',itemId:'sure'},{text:'No Way',itemId:'noway'}],
		fn:function(response){
		    console.log(response)
		}
	    });
	    msg.doComponentLayout();
	}	
    }
});
...

XTemplates and Sheets in Sencha Touch

24 Feb 2012

I was recently working on a project where there was specific help text that proved to be quite long in many cases. The original plan was to use Ext.Msg but that quickly became impractical due to the sheer amount of text. Next thing to use was Ext.Sheet which has the flexibility and options that I needed.

The Sheet ended up configured to have a toolbar and footer, docked to the top and bottom respectively. I further configured it to float in the center of the screen with about a 40 pixel margin around the sides. The lone item in the Sheet was a panel configured to use an XTemplate so that I could update it as needed with whatever help text came across the help web service.

The problem I encountered was that I could only populate the content panel within the Sheet once. It turns out that the activate listener for the content panel only ever fires one time – the very first time that the Sheet is displayed which is the first time the panel is activated.

After some thought this behavior makes sense. The panel is rendered the first time and the activate event fires. Thereafter, it doesn’t need to fire because it is always the lone active item of the sheet – it has already been activated/rendered. If I were to add another item to the sheet and switch between the two I would then get the activate event firing for both panels every time they are switched. As it is, the panel is activated the very first time out of necessity, activate naturally fires, and that is pretty much the end of it.

Back to the task at hand, I don’t have anything bound to stores, no proxies… I didn’t want to mess with that, I just wanted to push an object into the panel and I could only do it once with the panel’s activate event. Every other call to show the Sheet would not update the content with new text – only the very first item was ever displayed and all further use of the Sheet would only show the content that it was initially updated with.

See the following code which fits this scenario:

...
var helpSheet_tpl = new Ext.XTemplate(
    '',
        '
{data}
', '
', { compiled:true } ); var helpContent = new Ext.Panel({ id:'helpContent', layout:'card', scroll:'vertical', tpl:helpSheet_tpl, listeners:{ activate:function(e,o){ // this event will only ever fire once!!! helpContent.update(DATA); } } }); var helpSheet = new Ext.Sheet({ id:'helpSheet', cardSwitchAnimation:{type:'pop',duration:1000}, height: window.innerHeight - 40, width: window.innerWidth - 40, centered:true, floating:'true', layout: { type: 'card', align: 'stretch' }, items:[ helpContent ], dockedItems:[ { xtype:'toolbar', layout:'card', dock:'top', id:'help_header', title:'Help', }, { xtype:'button',text:'OK', layout:'card', dock:'bottom', listeners:{ tap:function(o,e){ helpSheet.hide(); } } } ] }); ...

The learning here is that the activate event for the above content panel will only fire once because it is the lone item – there is no item-switching happening and so therefore no opportunities for activate to fire again. Also, Ext.Sheet doesn’t have any events so you can’t put an activate event listener on it. At least, when I attempted this nothing happened at all. The Sencha docs imply that it is inherited but if you check the option to hide inherited you will see that there is nothing left event-wise for Ext.Sheet.

If you want to push an object into a panel in the above scenario by using the panel update() method you will have to do so outside of the panel, and not rely on the panel’s activate event. In my case I placed it within the JSONP callback. First I use the show() method to show the sheet and then I follow that up with the update() method on the panel.

XHR Post in Sencha Touch 1 & PhoneGap

13 Feb 2012

Sencha Touch has built in support for JSONP via “GET”, but if you have a lot of data to send you may quickly run into the GET character limit which varies across browsers. For a lot of data, “POST” is the preferred method and may be even preferred over GET for security reasons. The rub is that form POSTs will reload a page meaning that your app will reload and you’ll lose whatever state you had. That means we’ll have to do the POST via AJAX.

The astute among you might first ask about the cross domain policy and how it would prevent such a thing from happening (which JSONP by definition allows but is not useful here because its essentially a GET). As PhoneGap loads your Sencha Touch project via the file:// protocol cross domain XHR (XMLHttpRequest) is thus possible. Files loaded in this manner a free of the same domain policy.

With that aside it appears that Sencha Touch 1 just doesn’t have AJAX form posts as part of its framework but we can use Ext.Ajax.request() which allows us to set the desired form method and gives us success and error callbacks and even a timeout – which Ext.util.JSONP.request() lacks (bonus!).

The only additional setup is to add android.permission.INTERNET to the android manifest and to add the domain that you’re posting to to the whitelist in iOS. There has been some discussion about whitelisting in PhoneGap, it might be worthwhile to see how its evolved.

Here is a code snippet showing how the Ext.Ajax.request() method can be utilized within Sencha Touch:

...
Ext.Ajax.request({
	url: 'http://www.somewhere.com',
	method:'POST',
	params: 'configuration object goes here',
	scope:this,
	timeout:15000,
	disableCaching:true,
	failure: function(responseObj, opts){
		// handle your error response object here
	},
	success: function(responseObj, opts){
		// handle your success response object here
	}
});
...

The configuration object is simply something along these lines: {name:value,name:value,name:value,……}