Force Ext.data.Store to use GET

Say you have a simple Ext store:

var myStore = new Ext.data.Store({
  url:    '/widgets.json',
  reader: someReader
});

Which you put in a grid, along with a paging toolbar:

var myGrid = new Ext.grid.GridPanel({
  store:   myStore,
  columns: [.....],
  bbar:    new Ext.PagingToolbar({
    store: myStore
  })
  ... etc ...
});

Your grid loads up and the store performs a GET request to /widgets.json, which returns your widgets along with a total (see an example).

Awesome, but now we click one of the paging buttons on the PagingToolbar and we have a problem – our request has turned into POST /widgets.json, with “start=20” and “limit=20” as POST params.

Now we don’t really want that – we’re not POSTing any data to the server after all, we’re just trying to GET some. If you’re using a nice RESTful API on your server side this may cause you a real problem, as POST /widgets will likely be taken as an attempt to create a new Widget.

Luckily, as with most things the solution is simple if you know how. An Ext.data.Store delegates loading its data off to an Ext.data.DataProxy subclass. By default your store will create an Ext.data.HttpProxy using the url: ‘/widgets.json’ you passed in your store config. To make sure your stores are always requesting data using GET, just provide a proxy like this:

var myStore = new Ext.data.Store({
  proxy: new Ext.data.HttpProxy({
    url:    '/widgets.json',
    method: 'GET'
  }),
  reader: someReader
});

Adding a loading mask to your ExtJS application

Adding a loading mask like the one on the ExtJS API application is a nice way of showing the user that something is happening while their browser downloads the source code. It’s also extremely easy to do.

First, place the following HTML above all of your javascript include tags, ideally just after the <body> tag:

&lt;div id=&quot;loading-mask&quot;&gt;&lt;/div&gt;
&lt;div id=&quot;loading&quot;&gt;
  &lt;div class=&quot;loading-indicator&quot;&gt;
    Loading...
  &lt;/div&gt;
&lt;/div&gt;

If you are currently including javascript files inside the <head>, don’t – put them at the bottom.

With a bit of CSS (see below), this provides a white mask over all underlying content, and a loading message. When everything has loaded, remove the mask like this:

Ext.onReady(function() {
  setTimeout(function(){
    Ext.get('loading').remove();
    Ext.get('loading-mask').fadeOut({remove:true});
  }, 250);
});

The above simply fades out the HTML elements to reveal the now ready page. The setTimeout call gives your app a little time to render, which is useful if you’re doing something like pulling external content down from the server.

Finally, here’s the CSS I use to style up the loading mask. You’ll need to download a loading image and stick it in the appropriate directory.

#loading-mask {
  position: absolute;
  left:     0;
  top:      0;
  width:    100%;
  height:   100%;
  z-index:  20000;
  background-color: white;
}

#loading {
  position: absolute;
  left:     50%;
  top:      50%;
  padding:  2px;
  z-index:  20001;
  height:   auto;
  margin:   -35px 0 0 -30px;
}

#loading .loading-indicator {
  background: url(../images/loading.gif) no-repeat;
  color:      #555;
  font:       bold 13px tahoma,arial,helvetica;
  padding:    8px 42px;
  margin:     0;
  text-align: center;
  height:     auto;
}

Why you should be using History in your ExtJS applications

I’ve been making a few updates to the ExtJS API documents application recently. The actual updates include remembering which tabs you have open and using Ext.History to go between tabs (you can follow the forum post or see a beta version).

That’s not quite ready yet, but what has been made very clear to me is that any ExtJS application with more than one view should be using Ext.History. With History we get urls inside the application itself, we can parse them and dispatch accordingly. For example, I’m using a Rails-like Router, which lets you define an internal url map like this:

map.connect(&quot;:controllers/:action/:id&quot;);
map.connect(&quot;:controllers/:action&quot;);

The router knows how to decode urls based on the regular expression-like syntax above, and parse the matches into an object – for example:

#users/new    &lt;= becomes {controller: 'users', action: 'new'}
#users/edit/2 &lt;= becomes {controller: 'users', action: 'edit', id: 2}
#colours      &lt;= becomes {controller: 'colours'}

You can of course define any url matching scheme using the connect() function. I then use a simple Dispatcher, which looks at the decoded parameters. It finds the appropriate controller and calls that action on the controller, passing any other parameters as arguments. For example:

#users/new      &lt;= calls UsersController's &quot;new&quot; action
#colours/edit/2 &lt;= calls ColoursController's &quot;edit&quot; action, with {id: 2} as the argument

And so on. Each controller knows what to do for that action. It’s easy then to say to someone “go to http://myapp.com/admin#users/152/comments&#8221; – which will take them straight to the comments that user 152 has written. Compare that with saying: “go to http://myapp.com/admin, then click the List Users tab, then find the user called Joe Bloggs, then double click the bubble icon next to his name”. It’s obvious which approach is better.

You don’t even need to use something as elaborate as a router, just a simple switch statement or some regular expressions would be enough for many applications. Once you’ve got Ext.History setup, you could do something as simple as:

//decodes a url and decides how to dispatch it
dispatch = function(token) {
  switch (token) {
    case &quot;users&quot;    :   displayUsers();   break;
    case &quot;users/new&quot;:   displayNewUser(); break;
    case &quot;users/2/edit: editUser(2);      break;
    default:            displayDefault(); break;
  };
};
Ext.History.on('change', dispatch);

//Call dispatch on initial page load as Ext.History's change event is not fired here
Ext.History.init(function() {
  var token = document.location.hash.replace(&quot;#&quot;, &quot;&quot;);
  dispatch(token);
});

Obviously you don’t hard code user IDs like that but it’s easy to see how to roll your own. With just a few lines of code, you’ve decoded a url into a function to call, which can do anything you need it to. All your internal navigation needs to do is call Ext.History.add(“some/new/url”), which will now be picked up by your dispatch code.

It’s important to only route like this for idempotent actions (i.e. actions which display data rather than change it), so that data changing actions are not repeated. This is equivalent to using GET and POST correctly in normal web applications.

When the simplest implementation takes just a few lines of code, what reason could there be not to be using it?

ExtJS Solitaire

Update: We recently released the updated Touch Solitaire for Sencha Touch.


For a bit of fun over Christmas I thought I’d try my hand at writing Solitaire using the ExtJS library. The results of my efforts can be seen over at http://solitaire.edspencer.net.

It’s reasonably complete, with the familiar drag and drop moving of cards (and stacks of cards). Most of the interface is custom built, with classes representing Cards, Stacks, the Pack, etc. The main motivation for creating this is to give a real-world example of using Drag and Drop with Ext JS, as documentation for it can be hard to come by. The full source of the game can be found on github, and I encourage people to take a look at and/or improve the code if they wish.

A few stats: the game comes to 1300 lines of code, including generous comments and whitespace. It’s 15k minified, and uses a custom Ext build. It took roughly 25 hours to put together, which was mostly spent researching how to use Ext’s many D&D classes.

The reason I’m releasing it now is that I’m currently working on a much larger, more exciting open source ExtJS project which I want to concentrate on before releasing. If anyone wants to pick this up feel free to fork the code on Github or get in touch in the comments or in #extjs on IRC.

Using Ext.History

Ext.History is a small class that was released with ExtJS 2.2, making it easy to use the browser’s back and forward buttons without breaking your AJAX-only pages.

This can be really useful for any ExtJS application with more than one view, for example a simple app with a grid of Products, which can be double-clicked to reveal an edit form. Ext.History allows the user to click the back button to go back to the grid if they’re on the form, and even forward again from the grid. It does this by appending a token to the end of the url:

http://myurl.com/ (default url for the app)
http://myurl.com/#products (shows the products grid)
http://myurl.com/#products/edit/1 (shows the edit form for product 1)

This is useful, so let’s look at how to set it up. Ext.History requires that a form field and an iframe are present in the document, such as this:

&lt;form id=&quot;history-form&quot; class=&quot;x-hidden&quot; action=&quot;#&quot;&gt;
  &lt;div&gt;
    &lt;input id=&quot;x-history-field&quot; type=&quot;hidden&quot; /&gt;
    
  &lt;/div&gt;
&lt;/form&gt;

The div is just there to make the markup valid. Ext.History uses the iframe to make IE play nice. Generally I don’t like to make any assumptions about what is in the DOM structure so I use Ext to generate these elements:

/**
* Creates the necessary DOM elements required for Ext.History to manage state
* Sets up a listener on Ext.History's change event to fire this.handleHistoryChange
*/
initialiseHistory: function() {
  this.historyForm = Ext.getBody().createChild({
    tag:    'form',
    action: '#',
    cls:    'x-hidden',
    id:     'history-form',
    children: [
      {
        tag: 'div',
        children: [
          {
            tag:  'input',
            id:   Ext.History.fieldId,
            type: 'hidden'
          },
          {
            tag:  'iframe',
            id:   Ext.History.iframeId
          }
        ]
      }
    ]
  });

  //initialize History management
  Ext.History.init();
  Ext.History.on('change', this.handleHistoryChange, this);
}

Ext.History.fieldId and Ext.History.iframeId default to ‘x-history-field’ and ‘x-history-frame’ respectively. Change them before running initialiseHistory if you need to customise them (Ext.History is just a singleton object so you can call Ext.History.fieldId = ‘something-else’).

The main method you’ll be using is Ext.History.add(‘someurl’). This adds a token to the history stack and effectively redirects the browser to http://myurl.com/#someurl. To create something like the grid/form example above, you could write something like this:

Ext.ns('MyApp');

MyApp.Application = function() {
  this.initialiseHistory();

  this.grid = new Ext.grid.GridPanel({
    //set up the grid...
    store: someProductsStore,
    columns: ['some', 'column', 'headers'],

    //this is the important bit - redirects when you double click a row
    listeners: {
      'rowdblclick': {
        handler: function(grid, rowIndex) {
          Ext.History.add(&quot;products/edit/&quot; + rowIndex);
        }
      }
    }
  });

  this.form = new Ext.form.FormPanel({
    items: ['some', 'form', 'items'],

    //adds a cancel button which redirects back to the grid
    buttons: [
      {
        text: 'cancel',
        handler: function() {
          Ext.History.add(&quot;products&quot;);
        }
      }
    ]
  });

//any other app startup processing you need to perform
};

MyApp.Application.prototype = {
  initialiseHistory: function() {
    //as above
  },

  /**
   * @param {String} token The url token which has just been navigated to
   * (e.g. if we just went to http://myurl.com/#someurl, token would be 'someurl')
   */
  handleHistoryChange: function(token) {
    var token = token || &quot;&quot;;
    switch(token) {
      case 'products':        this.showProductsGrid();     break;
      case 'products/edit/1': this.showProductEditForm(1); break;
      case '':                //nothing after the #, show a default view
    }
  },

  showProductsGrid: function() {
    //some logic to display the grid, depending on how your app is structured
  },

  showProductEditForm: function(product_id) {
    //displays the product edit form for the given product ID.
  }
};

Ext.onReady(function() {
  var app = new MyApp.Application();
});

So when you visit http://myurl.com/#products, showProductsGrid() will be called automatically, and when you visit http://myurl.com/#products/edit/1, showProductEditForm() will be called with the argument 1. You can write your own logic here to change tab or show a window or whatever it is you do to show a different view to the user.

I’m not suggesting you parse the url token using a giant switch statement like I have above – this is only an example. You could get away with something like that for a very small app but for anything bigger you’ll probably want some kind of a router. That goes a little beyond the scope of this article but it is something I will return to at a later date.

There is also an example of Ext.History available on the Ext samples pages.

Custom containers with ExtJS

ExtJS has several built-in Container classes – classes which can contain one or more other Ext.Components (such as Grids, Forms, other Panels, etc). The most obvious example of a Container is the Ext.Panel class, along with its subclasses such as Ext.TabPanel, Ext.form.FormPanel and Ext.Window. With each container class you can add a bunch of components, like this:

//a child component to be added to the container below
var myComponent = new Ext.Panel({html: 'component 1'});

//Ext.Panel is a subclass of Ext.Container
var myPanel = new Ext.Panel({
  items: [
    myComponent,
    {html: 'component 2'},
    {html: 'component 3'}
  ]
});

Which will just create a Panel with three other Panels as its child components (‘panel’ is the default xtype, so we don’t have to specify it). More to the point, you can add and remove components from the Container like this:

myPanel.add({html: 'component 4'});
myPanel.remove(myComponent);

As myPanel is an Ext.Container subclass, the methods add() and remove() automatically add or remove child components from within the Container, and take care of any rendering that needs to be performed. Most of the time this is great, but what if you want to write your own custom Container? Say you had a bunch of shortcut links which performed some action in your application, and for styling or other reasons you want to put them into markup like this:

&lt;div class=&quot;x-shortcuts-wrapper&quot;&gt;
  &lt;div class=&quot;x-shortcuts-header&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;x-shortcuts&quot;&gt;
    &lt;!-- child components to go here --&gt;
  &lt;/div&gt;
  &lt;div class=&quot;x-shortcuts-footer&quot;&gt;&lt;/div&gt;
  &lt;button class=&quot;x-shortcuts-add&quot;&gt;Add&lt;/button&gt;
&lt;/div&gt;

You might write something like this:

Ext.ns('MyApp');
/**
 * @class MyApp.Shortcuts
 * @extends Ext.Container
 * Container for application shortcuts
 */
MyApp.Shortcuts = Ext.extend(Ext.Container, {
  /**
   * Creates the HTML markup for the shortcuts container
   * @param {Ext.Container} ct The container into which this container will be rendered
   */
  onRender: function(ct) {
    this.el = ct.createChild({
      cls: 'x-shortcuts-wrapper',
      children: [
        {cls: 'x-shortcuts-header'},
        {cls: 'x-shortcuts'},
        {cls: 'x-shortcuts-footer'},
        {cls: 'x-shortcuts-add', tag: 'button'}
      ]
    });
    
    MyApp.Shortcuts.superclass.onRender.apply(this, arguments);
    
    this.shortcutsHolder = this.el.child('.x-shortcuts');
  },
  
  //tells the container which element to add child components into
  getLayoutTarget: function() {
    return this.shortcutsHolder;
  }
});

So our onRender method is responsible for creating some markup, which must be assigned to this.el. We’re also calling the onRender() function of the superclass (Ext.Container) to make sure nothing is missed out.

The critical elements here are the getLayoutTarget() function, and the last line on onRender(). Usually when you subclass Ext.Container, the add() and remove() functions add and remove from this.el, which would result in something like this:

&lt;div class=&quot;x-shortcuts-wrapper&quot;&gt;
  &lt;div class=&quot;x-shortcuts-header&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;x-shortcuts&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;x-shortcuts-footer&quot;&gt;&lt;/div&gt;
  &lt;button class=&quot;x-shortcuts-add&quot;&gt;Add&lt;/button&gt;
  &lt;!-- child components will end up here --&gt;
&lt;/div&gt;

To prevent this from happening, we obtain a reference to the element we want components to actually be rendered to, and return that with getLayoutTarget(). After that the Container will once again do your bidding.

As of the time of writing getLayoutTarget() is not to be found anywhere in the Ext documentation (version 2.2), so my thanks go to Condor and Animal for answering my question on the ExtJS forum thread.

To round off the example, say your Shortcut class looked something like this:

/**
 * @class MyApp.Shortcut
 * @extends Ext.Component
 * Clickable shortcut class which renders some HTML for a standard application shortcut
 */
MyApp.Shortcut = function(config) {
  var config = config || {};
 
  //apply some defaults
  Ext.applyIf(config, {
    text: 'Shortcut Name',
    icon: 'default_shortcut.gif'
  });
 
  //call the superclass constructor
  MyApp.Shortcut.superclass.constructor.call(this, config);
};
Ext.extend(MyApp.Shortcut, Ext.Component, {
  onRender: function(ct) {
    this.el = ct.createChild({
      cls: 'x-shortcut',
      children: [
        {
          tag: 'img',
          src: this.initialConfig.icon
        },
        {
          tag:  'span',
          html: this.initialConfig.text
        }
      ]
    });
    
    MyApp.Shortcut.superclass.onRender.apply(this, arguments);
  }
});

Ext.reg('shortcut', MyApp.Shortcut);

Then our container would be created like this:

new MyApp.Shortcuts({
  items: [
    new MyApp.Shortcut({text: 'Shortcut 1', icon: 'shatner.gif'}),
    {xtype: 'shortcut', text: 'Shortcut 2', icon: 'nimoy.gif'},
    {xtype: 'shortcut'}
  ]
});

Which would produce HTML like this:

&lt;div class=&quot;x-shortcuts-wrapper&quot;&gt;
  &lt;div class=&quot;x-shortcuts-header&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;x-shortcuts&quot;&gt;
    &lt;div class=&quot;x-shortcut&quot;&gt;
      &lt;img src=&quot;shatner.gif&quot; /&gt;
      &lt;span&gt;Shortcut 1&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;x-shortcut&quot;&gt;
      &lt;img src=&quot;nimoy.gif&quot; /&gt;
      &lt;span&gt;Shortcut 2&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;x-shortcut&quot;&gt;
      &lt;img src=&quot;default_shortcut.gif&quot; /&gt;
      &lt;span&gt;Shortcut Name&lt;/span&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;x-shortcuts-footer&quot;&gt;&lt;/div&gt;
  &lt;button class=&quot;x-shortcuts-add&quot;&gt;Add&lt;/button&gt;
&lt;/div&gt;

JavaScript bra size calculator

One of the more mesmerizing websites I’ve worked on recently was for a lingerie boutique in the UK. Aside from the unenviable task of having to look at pictures of women in lingerie all day, I was also forced (forced!) to write a bra size calculator.

The theory behind bra size calculation is arcane and somewhat magical. Understanding of it does not come easily to man nor beast, so it is lucky that I, falling cleanly into neither category, have passed through pain and torment to save you the trouble.

Check it out.

Pleasing, no? The code looks like this, and can be found here:

var BraCalculator = {
  
  /**
   * The string to be returned when the result could not be calculated.
   */
  unknownString: &quot;Unknown&quot;,
  
  cupSizes: [&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;DD&quot;, &quot;E&quot;, &quot;EE&quot;, &quot;F&quot;, &quot;FF&quot;, &quot;G&quot;, &quot;GG&quot;, &quot;H&quot;, &quot;HH&quot;, 
             &quot;J&quot;, &quot;JJ&quot;, &quot;K&quot;, &quot;KK&quot;, &quot;L&quot;, &quot;LL&quot;, &quot;M&quot;, &quot;MM&quot;, &quot;N&quot;, &quot;NN&quot;],
  
  /**
   * Returns the correct bra size for given under bust and over bust measurements
   * @param {Number} underBust The measurement taken under the bust (in inches)
   * @param {Number} overBust The measurement taken over the bust (in inches)
   * @return {String} The correct bra size for the given measurements (e.g. 32C, 40DD, etc)
   */
  calculateSize: function(underBust, overBust) {
    var bandSize = this.calculateBandSize(underBust);
    var cupSize  = this.calculateCupSize(bandSize, overBust);
    
    if (bandSize &amp;&amp; cupSize) {
      return bandSize + cupSize;
    } else {
      return this.unknownString;
    };
  },
  
  /**
   * Calculates the correct band size for a given under bust measurement
   * @param {Number} underBust The measurement under the bust
   * @return {Number} The correct band size
   */
  calculateBandSize: function(underBust) {
    var underBust = parseInt(underBust, 10);
    return underBust + (underBust % 2) + 2;
  },
  
  /**
   * Calculates the Cup size required given the band size and the over bust measurement
   * @param {Number} bandSize The measured band size (should be an even number)
   * @param {Number} overBust The measurement taken over the bust
   * @return {String} The appropriate alphabetical cup size
   */
  calculateCupSize: function(bandSize, overBust) {
    var bandSize = parseInt(bandSize, 10);
    var overBust = parseInt(overBust, 10);
    var diff     = overBust - bandSize;
    
    var result   = this.cupSizes[diff][/diff];
    
    //return false if we couldn't lookup a cup size
    return result ? result : false;
  }
};

And to apply it to your own pages, use something a bit like this:

jQuery(document).ready(function(){
  //add listeners to band and cup measurement text boxes
  jQuery('#back').keyup(Honeys.updateBraSizeCalculation);
  jQuery('#cup').keyup(Honeys.updateBraSizeCalculation);
});

var Honeys = {
  updateBraSizeCalculation: function() {
    var back = jQuery('#back')[0].value;
    var cup  = jQuery('#cup')[0].value;
    
    if (back.length &gt; 0 &amp;&amp; cup.length &gt; 0) {
      jQuery('#fit')[0].value = BraCalculator.calculateSize(back, cup);
    };
  }
};

Now we’re talking UK sizes here, so exercise extreme caution! It should be trivial to adapt to your country with our lovely conversion charts.

Don’t pretend you’re not going to play with it. You know you are. Like, right now.

How Ext.apply works, and how to avoid a big headache

Ext.apply is one of those magic Ext JS methods which copies the essence of one object onto another. You usually call it like this:

Ext.apply(receivingObject, sendingObject, defaults)

Where defaults are optional. If you supply defaults, Ext.apply actually does this:

Ext.apply(receivingObject, defaults);
Ext.apply(receivingObject, sendingObject);

In other words, the order of precedence of the three arguments goes like this: any properties in receivingObject which are also present in defaults will be overwritten by the property in defaults. After that has happened, any properties which are present receivingObject (after defaults have been applied) and also present in sendingObject will be overwritten by the sendingObject value. More graphically:

Ext.apply({a: 'receiver'}, {a: 'sender'}, {a: 'default'}); // = {a: 'sender'}

For me, this was slightly unexpected as I expected the default options to have the lowest priority – that is the default option would only be copied across if it was not present in either the receiving or the sending objects, so watch out for that.

Anyway that’s all well and good once you know how it works inside, but while watching an otherwise excellent screencast from Jay Garcia (see http://tdg-i.com/42/ext-js-screencast-003-extapply-published), something odd happened. The example he gave went like this (commented lines signify the output returned by Firebug):

var obj1 = {x: 'x string', y: 'y string'}
// = {x: 'x string', y: 'y string'}

var obj2 = {a: 'a string', b: 4289, c: function(){}}
// = {a: 'a string', b: 4289, c: function(){}}

var obj3 = Ext.apply(obj2, obj1, {pxyz: 'soifje'})
obj3 
// = {a: 'a string', b: 4289, pxyz: 'soifje', x: 'x string', y: 'y string'}
obj2
// = {a: 'a string', b: 4289, pxyz: 'soifje', x: 'x string', y: 'y string'}
obj3 === obj2 
// true - obj3 and obj2 are the same object

var obj4 = Ext.apply(obj3, obj2, {a: 'fwaifewfaije'})
// obj4 = {a: 'fwaifewfaije', b: 4289, pxyz: 'soifje', x: 'x string', y: 'y string'}

So basically he set up obj1 and obj2 with non-conflicting properties, then merged them with some defaults to create obj3. In this case the defaults didn’t conflict with the properties from obj1 or obj2, so obj3 is essentially a straightforward combination of obj1 and obj2, plus a default pxyz value.

What he did then however was to create obj4 as a combination of obj2 and obj3, along with a default value for the ‘a’ property, which was a property of obj2 and obj3. Crucially, obj4’s ‘a’ property was set to the default value, which as we’ve seen from how Ext.apply works above, should never happen (it should be set to the default value but then immediately set back again on the second internal Ext.apply call).

So what gives? Well, it turns out this is because when calling:

obj3 = Ext.apply(obj2, obj1, {pxyz: 'soifje'})

obj3 and obj2 are the exact same object, as Ext.apply returns the first argument after the apply process has taken place. So in the next call:

obj4 = Ext.apply(obj3, obj2, {a: 'fwaifewfaije'})

obj3 and obj2 are in fact both references to the same object, which is causing the unexpected default value. We can show this by manually creating a new obj3 with the exact same properties, and running the example again:

var obj1 = {x: 'x string', y: 'y string'}
// obj1 = {x: 'x string', y: 'y string'}

var obj2 = {a: 'a string', b: 4289, c: function(){}}
// obj2 = {a: 'a string', b: 4289, c: function(){}}

var obj3 = {a: 'a string', b: 4289, pxyz: 'soifje', x: 'x string', y: 'y string'}
// obj2 = {a: 'a string', b: 4289, pxyz: 'soifje', x: 'x string', y: 'y string'}
// obj3 === obj2 =&gt; false... obj3 and obj2 are the same object

var obj4 = Ext.apply(obj3, obj2, {a: 'fwaifewfaije'})
// obj4 = {a: 'a string', b: 4289, pxyz: 'soifje', x: 'x string', y: 'y string'}

This time around we get what we expect – the default value is not applied because it is already present in obj2. Here, obj2 is not the same object as obj3, even though their properties are identical.

I’m not completely certain why two references to the same object cause this behaviour, but the code above does appear to demonstrate that this is what is happening (you can just copy/paste each example into Firebug to reproduce it).

Moral of the story? Well, now you have a more detailed understanding of Ext.apply, and hopefully you’ll be on your guard about referencing the same object by two different variables when performing this type of operation. I know I will be 😉

Cleaning up an example Ext JS form

One of my recent Ext JS forms had a section which looked like this:

items: [
  new Ext.Button({
    text: 'Preview Video',
    iconCls: 'play',
    handler: function() {
      var win;
      
      if (!win) {
        win = new Ext.Window({
          title: 'Preview Video',
          modal: true,
          height: 377,
          width: 368,
          items: [
            new Ext.Panel({
              autoLoad: '/admin/videos/' + video_id + '/preview.html'
            })
          ],
          buttons: [
            {
              text: 'OK',
              handler: function() {
                win.close();
              }
            }
          ]
        });
        
      };
      win.show();
      
    }
  })
]

Not horrific but not nice either – let’s DRY this up. It’s not too pleasant to read but all it’s really doing is rendering a customised Ext.Button which opens up a modal Ext.Window, in which is loaded the contents of a known url.

Ok so let’s start with that Window. First, we’ll make a subclass of Ext.Window:

/**
 * AdFunded.views.Video.PreviewWindow
 * @extends Ext.Window
 * A simple Preview window for the given video_id
 */
AdFunded.views.Video.PreviewWindow = function(config) {
  var config = config || {};
    
  Ext.applyIf(config, {
    title: 'Preview Video',
    modal: true,
    height: 377,
    width: 368,
    items: [
      new Ext.Panel({
        autoLoad: '/admin/videos/' + config.video_id + '/preview.html'
      })
    ],
    buttons: [
      {
        text: 'OK',
        scope: this,
        handler: function() {
          this.window.close();
        }
      }
    ]
  });
  
  AdFunded.views.Video.PreviewWindow.superclass.constructor.call(this, config);
  
  this.window = this;
};
Ext.extend(AdFunded.views.Video.PreviewWindow, Ext.Window);
Ext.reg('video_preview_window', AdFunded.views.Video.PreviewWindow);

Note the namespacing employed above – within an Ext MVC framework I have been developing across several projects for the last few months, all views follow this structure. AdFunded is the name of the application. The precise structure doesn’t matter here, but using a namespace for each app does.

So we’ve taken the Window setup out of our view now, which leaves us with:

items: [
  new Ext.Button({
    text: 'Preview Video',
    iconCls: 'play',
    handler: function() {
      var win;
      
      if (!win) {
        win = new AdFunded.views.Video.PreviewWindow({video_id: id});
      };
      win.show();
      
    }
  })
]

Great – we’ve gone from 34 lines in our view to 15, and scored ourselves a reusable Window component which we can call from anywhere in the app. Nice work, but there’s more to come… If we’re going to use the Preview Window again, we’ll probably need to use that Preview Button again too. Let’s see:

/**
 * AdFunded.views.Video.PreviewButton
 * @extends Ext.Button
 * Displays a Preview Window for the given video_id
 */
AdFunded.views.Video.PreviewButton = function(config) {
  var config = config || {};
  
  Ext.applyIf(config, {
    text: 'Preview Video',
    iconCls: 'play',
    handler: function() {
      var win = new AdFunded.views.Video.PreviewWindow({video_id: config.video_id});
      win.show();
    }
  });
  
  AdFunded.views.Video.PreviewButton.superclass.constructor.call(this, config);
};
Ext.extend(AdFunded.views.Video.PreviewButton, Ext.Button);
Ext.reg('video_preview_button', AdFunded.views.Video.PreviewButton);

Which leaves us with the following the the view:

items: [
  {
    xtype: 'video_preview_button',
    video_id: id
  }
]

We’ve now gone from 34 lines to 6 (in the view at least), but the point is not about cutting out lines of code – it’s creating reusable components. We’ve added 20 lines overall this way but we now have two extra components that we can call on at any time (with minimal lines of code), safe in the knowledge that they will provide a consistent experience each time.

ExtJS Radio Buttons and Square Brackets

While creating an ExtJS form with several radio buttons today I ran into a bug which caused none of them to work as expected, even though there were no errors/exceptions. To cut a long story short, it was because I was setting the name to “schedule[include_type]” – like this:

{
  xtype: 'radio',
  name: 'schedule[include_type]',
  inputValue: 'page',
  boxLabel: 'Show page:'
}

This radio button is one of 4, which allows the user which type of file they want to include on a particular model (a Schedule in this case) – be it Page, Video, Category or one other. The thing is – none of them work with the square brackets in the name. If you remove the brackets, they all work correctly, but the server-side is relying on those brackets to be present to group the data correctly.

In the end I bit the bullet and updated my submit method to add a new parameter directly – here’s a full example:

form = new Ext.form.FormPanel({
  items: [
    {
      xtype: 'radio',
      name: 'include_type',
      inputValue: 'page',
      boxLabel: 'Show page:'
    },
    {
      xtype: 'radio',
      name: 'include_type',
      inputValue: 'category',
      boxLabel: 'Show category:'
    },
    ... plus some extra items
  ],
  buttons: [
    {
      text: 'Save',
      handler: function() {
        
        //find the currently selected include_type from the form
        var include_type = this.form.getValues()['include_type'];
        
        //note the params option - this needs to be added manually otherwhise 
        //schedule[include_type] won't appear
        form.form.submit({
          waitMsg: 'Saving Data...',
          params: &quot;schedule[include_type]=&quot; + include_type,
          url: some url...
        });
      }
    }
  ]
})

Note: I don’t usually add buttons in the way above so I’m not sure if the form.form.submit will work correctly here – see http://extjs.com/deploy/dev/docs/?class=Ext.form.FormPanel for information about overriding submit.

So what we’re doing here is finding which radio button is currently checked, and appending this under “schedule[include_type]” when POSTing the form variables to the server. This really isn’t pleasant but seems to be the best way around this limitation for now.

I regularly use square brackets in other Ext JS Fields – Radio Buttons seem to be the only ones that have this problem. http://extjs.com/forum/showthread.php?p=185296 has a bit of background behind this, but no real solution.

%d bloggers like this: