Ext.create or Ext.widget or new… what the heck?

There has been a conversation going on in a private forum on the Sencha Forums where it discusses what to use and what to recommend in regards to how to create an instance of a class in Ext JS (which spills over to Sencha Touch as well). You can use Ext.create, Ext.widget or the new keyword and the end result is the same. So which should you use? If you ask me, it depends on the situation but the more important thing to me is knowing what happens under the hood with each method. The more you understand about the framework the better off you will be (so long as you don’t get overwhelmed which I will try not to do :) ). I’m not trying to sway you to use one method, I want you to know what happens under the hood and some drawbacks I see from each method.

The new keyword

The simplest, most performant form of creating an instance is using the new keyword:

new Ext.Component({
    html : 'Hello there'
});

This is the most performant means of creating an instance so why not always use it? Why have the other means? The first drawback of using the new keyword is that the class must always be loaded. In production this isn’t an issue as all classes should be loaded in a single (minified/obfuscated) file but in development the class may not be loaded. If the class isn’t loaded, a syntax error will be thrown. I would argue that you should setup your requires properly and this problem is now moot.

I personally always use the new keyword even when saving a couple CPU cycles doesn’t matter. To me it’s simpler and cleaner, the others are just a waste of extra code in a production build where performance really matters.

Ext.create

The most common (and the officially recommended means by Sencha) to create an instance is using Ext.create:

Ext.create('Ext.Component', {
    html : 'Hello there'
});

Not too different of a syntax from using the new keyword but there is more going on under the hood than just instantiating. The big drawback by using the new keyword is the class must already be loaded, Ext.create can be used even if the class has not been loaded, this is the reason why you pass in a string so it can look up if the class is loaded. If the class is not loaded, then Ext.create leans on Ext.Loader to synchronously load the class and then instantiate it.

Couple things here that are not optimal. First is the check to see if the class has been loaded. This is a simple object lookup but one that is not needed in production. The second thing is synchronously loading the class meaning no other javascript will execute while that class is loading meaning your application is frozen. This may not mean much locally as the Xhr completes very fast but I’ve seen projects where they deploy the development version of an app to a remote server that mimics the production server which may be slow to load the class. Also, when loading a class, Ext.Loader has to inspect the class to see if it has any dependencies to load which it will have to synchronously load them as well and the browser will only load 4 things at any given time so you may be waiting for the class and dependencies to load. In production, the loading shouldn’t happen as you should have everything compiled into a single file so you are left with the minimal object lookup to get the class from the string which isn’t the worst thing in the world.

I do use Ext.create when I have a string that I need to instantiate from. Imagine having a tree panel and clicking on a node you have the class name to instantiate. That class name is a string which Ext.create can handle.

Ext.widget

The last way to instantiate is using Ext.widget (do note this is only for components) which can be used like Ext.create (where you pass a string and an optional config object):

Ext.widget('component', {
    html : 'Hello there'
});

Or you can just use a config object which requires the xtype config:

Ext.widget({
    xtype : 'component',
    html  : 'Hello there'
});

This has the same drawbacks and benefits of Ext.create. The added drawback is that Ext needs to know about the xtype you are using in some way. This can be from loading the class or from using Sencha Cmd that creates the bootstrap information that tells Ext about the xtypes it finds. Ext keeps an object mapping xtypes to their class so it’s a simple object lookup but you also have added code to check if the first argument is a string or an object which isn’t that bad of a performance hit at all.

Summary

Even though I personally use the new keyword, I do some times have to use Ext.create when I only have a string like I mentioned before. I always make sure the classes I am using are already loaded, I stay away from sync loading at all costs.

I meant this blog not to sway you in any direction but to educate what happens under the hood for better clarification.

Share and Enjoy

  • Facebook
  • Twitter
  • Delicious
  • LinkedIn
  • StumbleUpon
  • Add to favorites
  • Email
  • RSS

Prevent browser zoom in Ext JS

Something that I’ve been seeing a lot lately is people reporting lots of layout issues when they use the browser zoom feature and unfortunately Ext JS does not support browser zoom. The sad part is it’s not 100% preventable for what I can see. I found I can prevent the zooming if you use the hotkeys (ctrl + -) but the browser will still allow zooming via the menu. Trying to disable the zooming via the menu I could not find a way cross browser (all IEs), would be elated if anyone knew a way for this cross browser. The following article was just out of curiosity if you could prevent the zooming at all.

Prevent via hotkeys

In Chrome, Safari, Firefox and IE you can hit ctrl and – or + to zoom out and in. Luckily you can listen for these keys and prevent the browser’s handling of these keys. First, we need add a keydown listener to the document body which is quite simple:

Ext.onReady(function() {
    Ext.getBody().on('keydown', function(e) {
        console.log('You pressed a key!');
    });
});

Run that in a browser like Chrome and press any key and you should see the log in the javascript console. Next we need to find out what the key codes are when you press the – and + keys which is quite simple:

Ext.onReady(function() {
    Ext.getBody().on('keydown', function(e) {
        console.log('You pressed a key!', e.getKey());
    });
});

Now, when you press a button it will also log out what key code is associated with that button. In Chrome, if you pressed – then the key code is 187 and + is 189. This is the same for Safari and IE but Firefox (shortened to Fx, that’s for you Jay) the keys are 61 and 173. The next step is to detect if the ctrl (and cmd on Mac) was held down when a key was pressed:

Ext.onReady(function() {
    Ext.getBody().on('keydown', function(e) {
        if (e.ctrlKey) {
            console.log('You pressed a key!', e.getKey());
        }
    });
});

Now, if you press ctrl and + it will log that you pressed a key but if you didn’t hold down the ctrl key then it will not log it. Let’s modify the conditional so that we can check out the key codes in a verbose way since Firefox is different than the others:

Ext.onReady(function() {
    Ext.getBody().on('keydown', function(e) {
        var key = e.getKey();

        if (e.ctrlKey && (
            (key == e.NUM_MINUS || key == e.NUM_PLUS)                   //Num Pad keys
                ||
            (Ext.isGecko && (key == 61 || key == 173))                  //Firefox
                ||
            ((Ext.isWebKit || Ext.isIE) && (key == 187 || key == 189))) // Chrome, Safari or IE
        ) {
            e.preventDefault();
        }
    });
});

Notice, I also added in the – and + from the num pad also. Also notice the e.preventDefault(); call, this is what will prevent the browser from handling those keys. If you run this snippet and try to zoom using the hotkeys the browser will not zoom. Sweet!

Other Notes

Firefox has the KeyboardEvent API which would be a great way to normalize the key differences if all browsers worked the same (KeyboardEvent.DOM_VK_EQUALS is 61 on Firefox, undefined on Chrome).

You don’t really need all the Ext.is* checks, the keys could be combined but I wanted to be verbose in the differences. This could be made better and there are libs that attempt to normalize the keys.

Share and Enjoy

  • Facebook
  • Twitter
  • Delicious
  • LinkedIn
  • StumbleUpon
  • Add to favorites
  • Email
  • RSS

Tiny Performance Boost in Data Drive Ext JS/Sencha Touch apps

If you’re like me, you probably try to squeeze every bit of performance out of your code and likely end up on a never ending search for performance, there are always little nooks and crannies to find more performance. Some times smaller code isn’t more optimized code and the thought of fewer function calls the better holds up quite a bit but still not always. A small tip I want to discuss is along the lines of fewer function calls within an Ext JS and Sencha Touch data Model. In this case, you may find a tiny performance boost but also knowing what is happening behind the scenes is always welcomed.

In an application, most of the time you will have data models in your application with something like:

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

    fields : [
        { name : 'firstName', type : 'string'                     },
        { name : 'lastName',  type : 'string'                     },
        { name : 'email',     type : 'string'                     },
        { name : 'phone',     type : 'string'                     },
        { name : 'age',       type : 'int'                        },
        { name : 'dob',       type : 'date', dateFormat : 'Y-m-d' }
    ]
});

Ok, likely you have many more fields but you get the point. I’m sure your application may still perform very well but you may not actually know what’s going on behind the scenes. When you define the type config within your field, you are telling the Model to convert the data coming in to that format. But couldn’t you expect firstName and lastName to always be a string so why convert it? If you do not specify a type, it will not do any conversion at all. If you specify your model like the next code snippet, functionality may be exactly the same but perform a little better:

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

    fields : [
        { name : 'firstName'                                },
        { name : 'lastName'                                 },
        { name : 'email'                                    },
        { name : 'phone'                                    },
        { name : 'age', type : 'int'                        },
        { name : 'dob', type : 'date', dateFormat : 'Y-m-d' }
    ]
});

In fact you can shorten that code down even more:

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

    fields : [
        'firstName',
        'lastName',
        'email',
        'phone',
        { name : 'age', type : 'int'                        },
        { name : 'dob', type : 'date', dateFormat : 'Y-m-d' }
    ]
});

Here, we care about the age and dob fields be a certain type, an Integer and Date. Now when the model instances are created, firstName, lastName, email and phone fields will not try to convert the value and will simple apply the value on the data object within your model. Ext.data.Field will turn your string into an Object for you.

Ok, run a perf test on it and you will likely see that performance didn’t improve significantly but I think it’s also very important to know what’s going on behind the scenes. If you set the type config, you are opting into a conversion. You need to evaluate if you actually need to opt for that conversion; some cases you may, some you may not.

Share and Enjoy

  • Facebook
  • Twitter
  • Delicious
  • LinkedIn
  • StumbleUpon
  • Add to favorites
  • Email
  • RSS

Ext 5 View Controller Event Listening

Right now, you fall into one of two groups: those that have Ext 5 and those that do not. Well, I hope you all get into the first group and get to experience the awesome that is Ext 5. Ext 5 is bringing sexy back! Ok, Justin Timberlake are lame I’m sorry but I’m serious! Even though I work for Sencha, I’m super excited about Ext 5 and all the new features and doohickeys it has. Today I want to talk about one of the new features which is to the MVC implementation.

When Ext 4 was released, among one of the major updates to the long standing framework was an MVC implementation. This implementation worked quite well but some were left wanting more. One issue I heard a lot was confusion on the controller, in fact I saw countless people making a single controller per view which in the Ext 4 MVC implementation wasn’t really how it was meant to work. The controllers were really global controllers that can handle any number of different views and their instances; I usually had one controller per feature. I will admit, I deployed this MVC implementation quite successfully and enjoyed developing Ext 4 applications (a huge improvement over Ext 3 and older trust me). But some people still wanted the 1:1 relationship of view to controller and Ext 5 aims to solve this with it’s view controller. Today I want to show an example of using a view controller and how to listen to events on the associated view.

It’s important to note, before we get started, that a view definition can have a view controller definition. That means when the view instance is created, it also instantiates a view controller instance and when the view instance is destroyed so is the view controller instance, they are coupled together through the view lifecycle. So if you have 4 instances of the same view, you will have 4 instances of the view controller.

Let’s begin!

First we need to generate an application using Sencha Cmd 5:

sencha generate app -ext MyApp /path/to/MyApp

This will create the default app which is basically a border layout with a west region and a tabpanel as a center region. Let’s make the west region a tree panel showing a file system. First lets create the /path/to/MyApp/view/FileTree.js file with this source:

Ext.define('MyApp.view.FileTree', {
    extend : 'Ext.tree.Panel',
    xtype  : 'myapp-filetree',

    requires : [
        'Ext.toolbar.Toolbar'
    ],

    store : {
        root : {
            text     : '/',
            expanded : true,
            children : [
                {
                    text : 'app.js',
                    leaf : true
                },
                {
                    text : 'index.html',
                    leaf : true
                }
            ]
        }
    },

    dockedItems : [
        {
            xtype : 'toolbar',
            dock  : 'top',
            items : [
                {
                    text : 'Delete'
                }
            ]
        }
    ]
});

We need to use that so we need to edit /path/to/MyApp/view/Main.js to look like:

Ext.define('MyApp.view.Main', {
    extend   : 'Ext.container.Container',
    requires : [
        'Ext.tab.Panel',
        'Ext.layout.container.Border',
        'MyApp.view.FileTree'
    ],

    xtype : 'app-main',

    layout : {
        type : 'border'
    },

    items : [
        {
            region : 'west',
            xtype  : 'myapp-filetree',
            title  : 'west',
            width  : 150
        },
        {
            region : 'center',
            xtype  : 'tabpanel',
            items  : [
                {
                    title : 'Center Tab 1'
                }
            ]
        }
    ]
});

Load this up in the browser and you will now see the west region has a file tree panel and if you have a keen eye you will also notice a Delete button but if you click it, it won’t do anything because we aren’t handling the click event. For this, we are going to add a click listener and create a view controller to handle the click listener. In /path/to/MyApp/view/FileTree.js file, find the Delete button config object (should just have the text config currently) and change it to have the listeners config:

{
    text      : 'Delete',
    listeners : {
        click  : 'onDelete'
    }
}

Notice the click listener is a string, this will map to the view controller automatically. Now we need to create the view controller at /path/to/MyApp/view/FileTreeController.js with this source:

Ext.define('MyApp.view.FileTreeController', {
    extend : 'Ext.app.ViewController',
    alias  : 'controller.myapp-filetreecontroller',

    requires : [
        'Ext.window.MessageBox'
    ],

    onDelete : function(button) {
        Ext.Msg.alert('Delete Clicked', 'You clicked on the Delete button!');
    }
});

Now that we created MyApp.view.FileTreeController we need to add that class to the requires array of MyApp.view.FileTree:

requires : [
    'Ext.toolbar.Toolbar',
    'MyApp.view.FileTreeController' //<-- added the view controller to the requires array
],

controller : 'myapp-filetreecontroller', //<-- instantiates controller via alias

So now load the app in the browser again and click that Delete button in the tree panel and you should then see the alert. Remember when we added the listener to the Delete button and we defined the click listener to the string? That string should match the method name in the view controller.

What if you didn’t want to have to add the listeners config to the button, the view controller should automatically listen to the click event without the view defining the listener. It’s quite easy and if you are used to Ext 4 global controllers this won’t be too unfamiliar. First, in /path/to/MyApp/view/FileTree.js we need to find that Delete button config that has the listeners config and remove the listeners so the button config looks like this again:

{
    text : 'Delete'
}

Now we need to edit /path/to/MyApp/view/FileTreeController.js to listen to the button’s click event so we can get back to seeing that alert message. For this we need to use the control config, here is the entire source for the FileTreeController.js file to show where the config object can go:

Ext.define('MyApp.view.FileTreeController', {
    extend : 'Ext.app.ViewController',
    alias  : 'controller.myapp-filetreecontroller',

    requires : [
        'Ext.window.MessageBox'
    ],

    config : {                             // 1
        control : {                        // 2
            'button' : {                   // 3
                click : 'onDelete'         // 4
            }
        }
    },

    onDelete : function(button) {
        Ext.Msg.alert('Delete Clicked', 'You clicked on the Delete button!');
    }
});

I’ve added four comments so I can easily describe what’s going on. The 1 commented line is where we define the config property, note that this is in the first level of that object. 2 commented line is the control config which is going to listen to events, this should be similar to using this.control in an Ext 4 controller. In the 3 commented line, we have ‘button’ as a string, the reason we have it as a string is to signify that this is actually an Ext.ComponentQuery selector. Technically you could remove the single quotes but I always keep them to show it’s a selector as other selectors may contain other characters that could be invalid like spaces. The 4 commented line is the click listener like we had before and it also is set to be a string which will map to the onDelete method on the class.

Run the updates in the browser and if you have done everything correctly you should once again see the alert. So now you can decide whether you want to define the listeners for any descendants in the view itself using the listeners config or in the view controller.

A caveat with using the listeners method of listening to the component events is that if you define listeners directly on the class (MyApp.view.FileTree for this example) is that it won’t get listened to automatically in the view controller like it does for descendant components. What I mean is if you tried to do something like this:

Ext.define('MyApp.view.FileTree', {
    extend : 'Ext.tree.Panel',
    xtype  : 'myapp-filetree',

    // ...

    listeners : {
        itemclick : 'onNodeClick'
    }
});

This will trigger the onNodeClick method you have in the view controller like you may expect. Another way to listen to the itemclick event on MyApp.view.FileTree is to use the control config in the view controller but use the selector ‘#’:

Ext.define('MyApp.view.FileTreeController', {
    extend : 'Ext.app.ViewController',
    alias  : 'controller.myapp-filetreecontroller',

    requires : [
        'Ext.window.MessageBox'
    ],

    config : {
        control : {
            '#'      : {
                itemclick : 'onNodeClick'
            },
            'button' : {
                click : 'onDelete'
            }
        }
    },

    onDelete : function() {
        Ext.Msg.alert('Delete Clicked', 'You clicked on the Delete button!');
    },

    onNodeClick : function(treeview, node) {
        Ext.Msg.alert('Node Clicked', 'You clicked on the node: ' + node.get('text'));
    }
});

Now once you refresh the browser and click on one of the tree nodes, you should then see the alert message saying you clicked on it.

Share and Enjoy

  • Facebook
  • Twitter
  • Delicious
  • LinkedIn
  • StumbleUpon
  • Add to favorites
  • Email
  • RSS

JsonP Explanation

Talking to a lot of people, I get the sense that there are quite a lot of people who don’t actually understand what JsonP is and how it works. In this blog post, I hope to provide some explanation behind JsonP as it’s truly a very simple concept.

Why would someone want to use JsonP?

JsonP is a means of transporting data from a client to a server across origins (different domains or ports). You can accomplish the same with an Xhr request and some headers from your server (I enjoy this site for help with CORS: enable-cors.org) but I’ve talked to many system admins and they don’t like adding the headers. With JsonP, you do not need to have any additional server setup, you can handle it in your endpoint (using PHP, Java or whatever language you are using). I personally like enabling CORS on my server as you can specify what origins to accept cross origin requests from where JsonP doesn’t have this security. Well, that’s not entirely true, your server side language can check where the request came from. And to be 100% honest, you don’t need a server admin to add a header to the server as a server side language can usually return a header (like the header() function in PHP) but I find that a bit awkward.

In a nut shell, what is JsonP?

Like I said in the second sentence of this blog, JsonP is truly a very simple concept. It has 2 parts, a Javascript function used as a callback that you define on the client and a <script< element you have in your HTML page. The <script> element will handle the transport of data and the callback function handles the data when it has been loaded. That’s it. In fact here is an example:

<html>
    <head>
        <script type="text/javascript">
            window.MyCallback = function(data) {
                //handle the data returned here
            };
        </script>
        <script type="text/javascript" src="http://mydomain.com:8080/data.php?callback=MyCallback"></script>
    </head>
    <body></body>
</html>

Like I said, we have a Javascript function that is used as a callback, I’ve named mine MyCallback and you have a <script> element that does the actual loading. Pretty simple right? There is no smoke and mirrors, there is no Xhr request going on that you need to set up.

So this is how Ext JS’ Ext.data.JsonP works?

It’s exactly how Ext.data.JsonP works. If you execute this code in Ext JS:

Ext.data.JsonP.request({
    url : 'http://mydomain.com:8080/data.php'
});

All it’s doing is creating the callback function (which it will append to the url you provided so no need to worry about that) and inserting that <script> into the <head> of your HTML document. It does other things like enables a timeout, keeps track of the requests, executes a success/failure function you can provide to the request method and cleans up the <script> after loading.

How do I handle this on the server?

Like I said, this request no server setup, it’s all handled in your server side language of choice. The request will have a callback request parameter and it’s value should wrap your “Json” data which actually will execute the Javascript callback function that is supposed to be created in the client app. Let’s use my first code that we used when describing JsonP in a nutshell. It’s sending the callback parameter with a value of MyCallback so our “Json” needs to be wrapped in MyCallback so that we execute the MyCallback function. Here is an example of the response:

MyCallback({
    "foo":  "bar"
});

Looks exactly like Javascript doesn’t it? That’s because it is Javascript. Remember, the <script> does the loading which will expect Javascript. It’s just like if you were loading a somefile.js with a <script> only it’s Javascript returned from a server side language instead of some file on your server.

My server side language of choice is PHP. Here is a sample PHP script that would support JsonP:

<?php
$isJsonP = isset($_REQUEST['callback']);
$data    = array('foo' => 'bar');

if ($isJsonP) {
    echo $_REQUEST['callback'] . '(';
}

echo json_encode($data);

if ($isJsonP) {
    echo ');';
}
?>

In this code, if there is a callback parameter the $isJsonP variable will be true which will wrap the json that is echoed out with the callback function and the needed parentheses. This will echo out the example JsonP response from above.

Let’s summarize this up.

JsonP is a means of transporting data across different origins. All JsonP is is a Javascript function and a <script> element which makes JsonP compliant with any browser that can load Javascript files with absolutely zero setup.

Share and Enjoy

  • Facebook
  • Twitter
  • Delicious
  • LinkedIn
  • StumbleUpon
  • Add to favorites
  • Email
  • RSS