More often than not when defining a new class, we want to extend an existing Ext JS class or component so that we inherit its current behavior and add our own new functionality.
This recipe explains, how to extend an existing class and add new functionality through new methods and by overriding existing ones.
We will define a very simple class that models a Vehicle
, capturing its Manufacturer
, Model
, and Top Speed
. It has one method called travel
, which accepts a single parameter that represents the distance to be travelled. When called, it will show an alert with details of the vehicle, how far it travelled, and at what speed.
Define our base
Vehicle
class, which provides us with our basic functionality and from which we will extend our second class:Ext.define('Cookbook.Vehicle', { config: { manufacturer: 'Unknown Manufacturer', model: 'Unknown Model', topSpeed: 0 }, constructor: function(manufacturer, model, topSpeed){ // initialise our config object this.initConfig(); if(manufacturer){ this.setManufacturer(manufacturer); } if(model){ this.setModel(model); } if(topSpeed){ this.setTopSpeed(topSpeed); } }, travel: function(distance){ alert('The ' + this.getManufacturer() + ' ' + this.getModel() + ' travelled ' + distance + ' miles at ' + this.getTopSpeed() + 'mph'); } }, function(){ console.log('Vehicle Class defined!'); }); var vehicle = Ext.create('Cookbook.Vehicle', 'Aston Martin', 'Vanquish', 60); vehicle.travel(100); // alerts 'The Aston Martin Vanquish travelled 100 miles at 60mph
Define a sub-class
Cookbook.Plane
that extends our baseVehicle
class and accepts a fourth parameter ofmaxAltitude
:Ext.define('Cookbook.Plane', { extend: 'Cookbook.Vehicle', config: { maxAltitude: 0 }, constructor: function(manufacturer, model, topSpeed, maxAltitude){ // initialise our config object this.initConfig(); if(maxAltitude){ this.setMaxAltitude(maxAltitude); } // call the parent class' constructor this.callParent([manufacturer, model, topSpeed]); } }, function(){ console.log('Plane Class Defined!'); });
Create an instance of our
Cookbook.Plane
sub-class and demonstrate that it has the properties and methods defined in both theVehicle
andPlane
classes:var plane = Ext.create('Cookbook.Plane', 'Boeing', '747', 500, 30000); plane.travel(800);
Alerts The Boeing 747 travelled 800 miles at 500mph (inherited from the Vehicle class)
alert('Max Altitude: ' + plane.getMaxAltitude() + ' feet');
Alerts 'MaxAltitude: 30000 feet' (defined in the Plane
class)
The extend
configuration option, used when defining your new subclass, tells the Ext.Class'
Extend preprocessor (which we talked about in the previous recipe) what class your new one should be inherited from. The preprocessor then merges all of the parent class' members into the new class' definition, giving us our extended class.
By extending the Vehicle
class in this way our class diagram will look like the one shown as follows. Notice that the Plane
class still inherits from the Ext.Base
class through the Vehicle class' extension of it:
The callParent
method is a very quick way of executing the parent class' version of the method. This is important to ensure that the parent class is constructed correctly and will still function as we expect. In previous versions of Ext JS, this was achieved by using the following syntax:
Plane.superclass.constructor.apply(this, arguments);
The new callParent
method effectively still does this but it is hidden from the developer, making it much easier and quicker to call.
We can expand on this idea by adding new functionality to the Plane
class and override the base class' travel
method to incorporate this new functionality.
A plane's travel
method is a little more complicated than a generic vehicle's so we're going to add takeOff
and land
methods to the class:
Ext.define('Cookbook.Plane', { ... takeOff: function(){ alert('The ' + this.getManufacturer() + ' ' + this.getModel() + ' is taking off.'); }, land: function(){ alert('The ' + this.getManufacturer() + ' ' + this.getModel() + ' is landing.'); } ... });
We can then override the travel
method of the Vehicle class to add in the takeOff
and land
methods into the Plane's travel
procedure:
Ext.define('Cookbook.Plane', { ... travel: function(distance){ this.takeOff(); // execute the base class’ generic travel method this.callParent(arguments); alert('The ' + this.getManufacturer() + ' ' + this.getModel() + ' flew at an altitude of ' + this.getMaxAltitude() + 'feet'); this.land(); } ... });
This method extends the travel
functionality given to us by the Vehicle
class by alerting us to the fact that the plane is taking off, flying at a specific altitude, and then landing again.
The important part of this method is the call to the callParent
method. This executes the base class' travel
method, which runs the Vehicle
's implementation of the travel
method. Notice that it passes in the arguments
variable as a parameter. This variable is available in all JavaScript functions and contains an array of all the parameters that were passed into it.
We can see this in action by creating a new Plane
object and calling the travel
method:
var plane = Ext.create('Cookbook.Plane', 'Boeing', '747', 500, 30000); plane.travel(800); // alerts 'The Boeing 747 is taking off' // 'The Boeing 747 travelled 800 miles at 500mph' // 'The Boeing 747 flew at an altitude of 30000 feet' // 'The Boeing 747 is landing.'
The very first recipe in this chapter that covers how classes work.
The recipe describing Dynamically loading Ext JS classes, which teaches you about how these classes can be loaded on the fly.
The Extending Ext JS components recipe, which explains how to use inheritance to extend the functionality of the framework.