3. Ext JS and OO-Javascript

JavaScript is not a real object-oriented language, it is a prototyping language, that is the difference with other object-oriented languages. In Javascript, you don’t use classes, you create objects from other objects.

When developing your own plugins, it’s a good idea to place all of your classes and singletons into namespaces to avoid collisions with other developers’ code. With Ext JS, it’s easy to do this using Ext.namespace.

3.1. Classes

To declare a new class, one usually starts with defining the constructor. It is customary, but not required, to have a “configuration” object as the first parameter. Configuration parameters are object/value pairs that configure specific parts of an object’s instantiation, avoiding large sparse parameter lists. The constructor calls the constructor of its parent manually in case the class doesn’t have its native constructor. If it does have one, then the following code should be executed to call the constructor, depending on which parameters you need to pass:

My.NameSpace.ObjectClass.superclass.constructor.call(this, config)

or

My.NameSpace.ObjectClass.superclass.constructor.apply(this, arguments)

This is the way all the classes in WebApp should be created.

We’ll use the Spreed plugin code as an example, with a snippet of the code of the class SpreedParticipantBox:

Ext.namespace('Zarafa.plugins.spreed.dialogs');

/**
 * @class Zarafa.plugins.spreed.dialogs.SpreedParticipantBox
 * @extends Zarafa.common.recipientfield.ui.RecipientBox
 * @xtype zarafa.spreedparticipantbox
 *
 * Extension to the  {@link Zarafa.common.recipientfield.ui.RecipientBox}.
 * This box offers adding moderator icon for the moderator participant.
 */
Zarafa.plugins.spreed.dialogs.SpreedParticipantBox = Ext.extend(
        Zarafa.common.recipientfield.ui.RecipientBox, {

        /**
         * @constructor
         * @param config Configuration object
         */
        constructor : function(config)
        {
                config = config || {};
                Ext.applyIf(config, { });
                Zarafa.plugins.spreed.dialogs.SpreedParticipantBox.superclass.constructor.call(this, config);
        },

        // other functions
        }
);

Ext.reg('spreed.spreedparticipantbox',
Zarafa.plugins.spreed.dialogs.SpreedParticipantBox);

The Ext.namespace call on the first line ensures that there exists a JavaScript object called Zarafa.plugins.spreed.dialogs to act as an enclosing namespace object for our new class. The Ext JS Ext.namespace function declares a namespace. We place chunks of related functionality in namespaces and let the code tree reflect the namespace hierarchy.

After this come several lines of documentation: the class name, its parent class and xtype to register. It’s good practice to always do this since the build system extracts this information to determine the inclusion order of JavaScript files.

The call to Ext.extend expresses that Zarafa.plugins.spreed.dialogs.SpreedParticipantBox extends (is a child class of) Zarafa.common.recipientfield.ui.RecipientBox. It is now possible to substitute an instance of the former for the latter. In most cases you can easily extend already existing classes to add some small functionality. If the class you’re writing is not a child class of anything, simply extend it from Object.

When creating a common user interface class in the core, it should be registered using the Ext.reg function to allow the usage of xtype when creating the object. It is registered with the prefix Zarafa.[name] to prevent name clashes with Ext JS registered classes.

// At the bottom of the derived class, register it:
Ext.reg('spreed.spreedparticipantbox',Zarafa.plugins.spreed.dialogs.SpreedParticipantBox);

// When using the derived editor in any class:
xtype : 'spreed.spreedparticipantbox'

New classes should always be created in a new file within the correct folder. Exceptions might be made if the new class is a helper class which only consists of a few lines of code and it is bound to the other class, defined in the same file, in such a way that moving it into a separate file is not logical.

3.2. Accessing Components

Often, when writing Panels which contain Components combined with the lazy instantation as discussed further, the problem arises that somewhere in the code of the container, a particular component must be accessed. Because the items array cannot be read (this only contains the lazy configuration objects and not the Component instantiations) other methods like panel.findBy(), findById() or findByType() must be used. Because these functions always search through all items, and all subitems (in case containers are embedded in the main panel), performance of these functions is quite low.

Ext JS offers an easy way to assign a component to a variable in the panel during rendering. If the ref option is used in the component, the variable name can be specified of the component inside the parent panel. By using a path specifier it is even possible to assign the variable to the parent of the parent container. Consider the following example:

{
        xtype: 'panel',
        id: 'panel1',
        items: [{
                xtype: 'textarea',
                ref: 'myTextArea'
        }]
}

After rendering, the panel, with id panel1, will contain the field myTextArea, which will point to the given textarea. For specifying the path, consider the following example:

{
        xtype: 'panel',
        id: 'panel1',
        items: [{
                xtype: 'panel',
                id: 'panel2',
                items: [{
                        xtype: 'textarea',
                        ref: '../myTextArea'
                }]
        }]
}

By using the path seperator, the field myTextArea will now be assigned to panel1. Within the textarea itself, panel1 can be accessed through the refOwner field.

Using the above objects, one can use the following code within the textarea:

reset : function()
{
        // The panel owns the record for which we display the data
        this.setText(this.refOwner.record.get('data'));
}

While in panel1, we can use the following code to access the textarea component:

update : function(record)
{
        // Update the textarea with the new record data
        this.myTextArea.update(record.get('data'));
}

This will work for any component, including components added in the tbar (top bar), fbar (footer bar) or bbar (bottom bar) fields. However, for these fields, additional path seperators are needed, since these are actually separate containers inside the main container.

3.3. Enums

Enumerations in Zarafa are extended from Zarafa.core.Enum. See the following example from the Spreed plugin:

Ext.namespace('Zarafa.plugins.spreed.data');

/**
 * @class Zarafa.plugins.spreed.data.DialogTypes
 * @extends Zarafa.core.Enum
 *
 * Enum containing the different types of dialogs needed to display spreed meeting.
 * @singleton
 */
Zarafa.plugins.spreed.data.DialogTypes = Zarafa.core.Enum.create({

        /**
         * The dialog with empty fields.(Brandly new)
         *
         * @property
         * @type Number
         */
        EMPTY  : 1,

        /**
         * The dialog with filled subject and participants.
         *
         * @property
         * @type Number
         */
        FILLED : 2,

        /**
         * The dialog with only participants field prefilled.
         *
         * @property
         * @type Number
         */
        PARTICIPANTS_FILLED : 3
});

So, this example contains, as usual, the namespace specification, then comments explaining what the purpose of this enumeration is. Then, the constructor create of the superclass, followed by the list of properties and their values.

You can also add extra items to an enumeration at a later stage, this might be especially helpful for plugins. This requires to extend from Zarafa.core.data.RecordCustomObjectType. It is connected with RecordFactory, so all custom records get a BASE_TYPE that is higher than 1000; so you can see it is a custom record. Thus, BASE_TYPE is equal to 1000, and the next one you add is then 1001, as the value of the previous highest value is incremented. See an example of its use in the class FbEventRecord:

Zarafa.core.data.RecordCustomObjectType.addProperty('ZARAFA_FACEBOOK_EVENT');

Now our Facebook records are of our custom type ZARAFA_FACEBOOK_EVENT.

3.4. Singletons

Static functions can be declared inside a singleton object. A singleton, in object oriented design, is a class that only has a single instance. This can be easily simulated in JavaScript by just creating one instance and re-using it. Consider the following example of the definition of a singleton, emulated in Javascript by a struct:

/**
 * @class Zarafa.core.XMLSerialisation
 * Functions used by Request for converting between XML and JavaScript objects
 * (JSON)
 * @singleton
 */
Zarafa.core.XMLSerialisation = {
        // snip
};

For singleton classes which extend an existing class, consider the following example:

/**
 * @class Ext.StoreMgr
 * @extends Ext.util.MixedCollection
 * The default global group of stores.
 * @singleton
 */
Ext.StoreMgr = Ext.extend(Ext.util.MixedCollection, {
        // snip
});

// Make it a singleton
Ext.StoreMgr = new Ext.StoreMgr();

There is now an instance of Ext.StoreMgr that is given the same name of the original class definition, effectively making it the only possible instance of it.