What happened getForm().getValues() returns a dom error in extjs

Background:

Ok let’s assume for a minute that you have a tab panel with four tabs each containing a form. You have placed this panel inside of a window, and the goal is to have one ‘save’ button on the bottom of the window that will save all of the forms simultaneously so the user does not have to remember to save after changing data on a tab before moving to the next. This is a common and straight forward task, and I am sure that it comes up in the real world often; I’ve used this layout multiple times myself.

The Problem:

You want to read the form values and consolidate them into one call to your backend database, however, then form elements in the tab panels behind the default panel have not been rendered to the dom, therefore, you cannot access them via getValues().
You want to set the values in each form; however, you cannot set values for items that do not exist.

The Solution:

The code below will construct a button that can be placed at the ‘Window’ level and will try to get the values of all child forms contained in a tab panel inside the window.

buttons:[
    {
    text    : 'Save',
    listeners : {
        click : function() {
            // The first form is always rendered!
            var first_form_values  = first_form.getForm().getValues(false),
                // This checks the form first to see if they have been rendered
                second_form_values = !second_form.rendered ? '' : second_form.getForm().getValues(false),
                third_form_values  = !third_form.rendered  ? '' : third_form.getForm().getValues(false),
                fourth_form_values = !fourth_form.rendered ? '' : fourth_form.getForm().getValues(false);

            Ext.Ajax.request({
                url:'your_url.php?' +
                    // You need urlEncode as getValues passes
                    // back an object!
                    '&' + Ext.urlEncode(first_form_values)  +
                    '&' + Ext.urlEncode(second_form_values) +
                    '&' + Ext.urlEncode(third_form_values)  +
                    '&' + Ext.urlEncode(fourth_form_values),
                method:'POST',
                success:function () {
                    msg('','Your changes have been saved successfully!');
                },
                failure:function () {
                    msg('uh-oh','Your changes have not been saved!');
                }
            });
        }}
    }
]

That’s all there is to sending your data values from multiple forms to the server in one ajax post, but what about getting values from the server and updating your forms that reside in the tab panel?

Luckily, when an element is in the tab panel you can add an ‘activate‘ listener that is waiting for the tab and form to become visible, and the best part is it is fired every time you click on the tab, which would be useful as afterrender is called once, and that’s when the component is first rendered (as the name suggests ).
The code below will load values to your form and must be placed at the formPanel element level.

listeners   :{
                    'activate': function(e){
                        working_status_form.getForm().load({
                            url: '/getData.php',
                            failure: function(form, action) {
                                Ext.Msg.alert('uh-oh','Your data has not been loaded.');
                            }
                        });
                    }
                },

Handling user input at the source, an example using DUNS numbers and mod 10 check digits with extjs

Right now you must be thinking “what and the hell is a duns number?” or “I can’t believe my luck, I was looking for an easy way to verify duns numbers on the client side in my app!”. If you are in the first camp, then let me elaborate. The D-U-N-S number is a unique identifier used for keeping track of businesses, and a business is actually required to have one if they are to do work for the US Government, Australian Government, the United Nations, or the European Commission, so yes, you could say that it widely used. What’s really cool for UI developers about the D-U-N-S number is the fact that it is set up with a check digit and you can perform some calculations on the data entered in your apps D-U-N-S field to make sure that the entry is valid. I am sure I don’t have to tell you that eliminating bad data entry is critical to ensuring data integrity, and as with most things in life, it is often easier to take care of issues at the source of the problem before they grow, rather than spend time later trying to clean up the mess.

To set this check up in Extjs we will need to create a custom vType (http://docs.sencha.com/ext-js/4-1/#!/api/Ext.form.field.VTypes ).

The code is below.


var dunsNbr     = /\d{2}-?\d{3}-?\d{4}/i;
   
// vtype for Duns Number including MOD 10 Check
    Ext.apply(Ext.form.field.VTypes, {
        //  vtype validation function
        duns: function(val, field) {
            if(dunsNbr.test(val) == true){
                return mod10CheckDigit(val);
            } else {
                return false;
            }
        },
        // vtype Text property: The error text 
        // to display when the validation 
        // function returns false
        
        dunsText: 'The D-U-N-S® number is always' +
            ' presented in a distinct format:' +
            ' two digits, hyphen, three digits,' +
            'hyphen, four digits. ' +
            'For example: 04-997-7473.',
        // vtype Mask property: The keystroke filter mask
        dunsMask: /[\d\-]/i
    }); 

In the code above note that duns is the vtype, and the dunsText will be displayed in a qTip when the user enters the wrong input. The dunsMask is used to filter out keystrokes that do not match the pattern provided. It is important to realize that the mask is not a regex value that matches the specific string that you would like to see in your duns field, but is actually regex that lists the allowed characters in the field. This is a key point!

Now, take a look at the line of code:

if(dunsNbr.test(val) == true){ …}

. This is the code that performs the regex check on the value that has been entered. dunsNbr is declared at the top of our vType. You can find more information about Ext’s regex features here (http://docs.sencha.com/ext-js/4-1/#!/api/RegExp), but the points to remember are 1) you have to add a / before the regex statement and a /i at the end of the statement and 2) never try to plug your regex into the ‘Mask’ property.

I find that it’s easier to test my regex code on a website like: http://www.zytrax.com/tech/web/regex.htm#experiment before I put it in my javascript.

Now suppose you get through the check above, you will notice that the very next line reads:

                return mod10CheckDigit(val);

In order to check the D-U-N-S number you would take the first 8 digits in the number. Next loop through the numbers and multiply the even numbers by 2. Now the numbers greater than 10 are separated and you write out the ones and tens place and add all of the numbers together.

D-U-N-S with check digit in the 9th place = 0 4 9 9 7 7 4 73

0 4 9 9 7 7 4 7
x1 x2 x1 x2 x1 x2 x1 x2
0+8+9+1+8+7+1+4+4+1+4 = 47

In this example above , the total is 47. Subtract this total, 47 from the next highest multiple of 10, (that is 50 in this case).

50 – 47 = 3

3 = check digit

The code below uses Modulus 10 + 5, meaning if the original check digit calculation is greater than a 4, then add 5 and then subtract 10 to determine the check digit.

That’s it! So with this quick tutorial you have seen how to apply a function and some serious calculations to an Extjs vType, how you can use regex, and what Mask is all about, and how you can ensure that data gets checked at the client side before hitting your database.

The function you need is below:

 function mod10CheckDigit(value) {
        //Strip all characters except numbers
        var v   = value.replace(/[^0-9]+/g,''),
            cd  = v.substr(8,1),
            v   = v.substr(0, 8),
            total = 0,
            temp,
            tens,
            ones,
            modDigit;

        //Get Odd Numbers
        for (i=0; i <= v.length-1; i=i+2) {
            total = total + parseInt(v.substr(i,1));
        }

        //Get Even Numbers
        for (i=1; i<=v.length; i=i+2) {
            temp = parseInt(v.substr(i,1)) * 2;
            if (temp > 9) {
                tens = Math.floor(temp/10);
                ones = temp - (tens*10);
                temp = tens + ones;
            }
            total = total + temp;
        }

        //Determine the checksum
        modDigit = (10 - total % 10) % 10;

        // Now if the original check digit calculation is greater than a 4, then add
        // 5 and then subtract 10 to determine the check digit

        if (modDigit > 4){
            modDigit = ((parseInt(modDigit) + 5) - 10);
        } 

        // Duns Number Expansion OR original MOD 10 check
        if ( modDigit == cd){
            return true;
        } else {
          //  alert(modDigit + ' - ' + cd);
            Ext.Msg.alert('Invalid D-U-N-S® Number', 'The D-U-N-S® number you entered was in the correct format, however, did not pass the D-U-N-S® validation test. Please correct your input. ')
            return false;
        }
    }

Stop the backspace key from going to the previous page in your extjs app

If you have an app with just one view and there is even a small possibility that the user will open the app in a browser that they have been using to cruise around the web and the user will likely use the backspace key in your app then you should think about turning off the backspace == url -1 feature in most browsers.

If you actually want to use the backspace button for its intended purpose and you would like to map ext events to the browser history then a good choice might be using Ext.History, which you can learn about in depth by reading Ed Spencer’ blog post its use. (http://edspencer.net/2009/01/why-you-should-be-using-history-in-your.html )

Turning off the backspace key is pretty straight forward. You would just use Ext’s EventManager and create a listener to check for any keypress events that take place on the page. If the user is not in an input field then you would ignore the default action of the backspace button. Placing the code below right under your Ext.onReady … method will do the trick.

Ext.EventManager.addListener(Ext.getBody(), 'keydown', function(e){
        if(e.getTarget().type != 'text' && e.getKey() == '8' ){
            e.preventDefault();
        }
    }); 

Dealing with toolbars that go on forever

This should not be a shocker to anyone, but the amount of toolbar that is visible on a users machine varies greatly with screen resolution. Best case scenario, the users that you have in mind all use a computer just like your development machine, and all have the great sense to set their resolution just like yours, then of course you would be guaranteed that they would see every single toolbar item just as you meticulously laid them out. Worst case scenario, well, you could get lucky and they call the help desk asking where that button disappeared to, or maybe they just walk to the competing product, you will never know. Fortunately one simple setting will put your invisible toolbar items in a dropdown menu to the right of the visible toolbar items. The extjs example can be found here (http://docs.sencha.com/ext-js/4-1/#!/example/toolbar/overflow.html).

The code that makes this possible is:

layout: {
      overflowHandler: 'Menu'
}

Help I can’t set values in my Multiselect Combobox in extjs4

Ok, there is a little documented trick to setting multiple values in the extjs4.1 combobox when you have multiselect set to true and you are returning your values as a comma delimted string from the store. I know what you are thinking, and no I don’t mean you are storing your multiple values in the database as a comma delimited string, but let’s say you used STUFF or something like that to get the values back as a nice delimited string.

Your data would look like this if you alerted the value in the record.

Your combobox would look like this after the record was loaded:

But you want the combobox to look like this:

What you will need to do is transform your comma delimited string into an array and then set the value like this:

// this goes right before your form.loadRecord(record) code

var values = rec.get('delimited_string_field').split(',');
Ext.getCmp('your_multiselect_box').setValue(values);   

It’s that easy!