Skip to content

Latest commit

 

History

History
2269 lines (1696 loc) · 125 KB

File metadata and controls

2269 lines (1696 loc) · 125 KB

Developing Web Applications With Ext JS Framework

In the previous chapter you’ve got familiar with the JavaScript library jQuery. Now we’ll introduce you to a more complex product - a JavaScript framework Ext JS from Sencha. This is one of the most feature-complete frameworks available on the market, and you should give it a serious consideration while deciding on the tooling for your next enterprise HTML5 application.

Providing detailed coverage of Ext JS framework in on chapter is mission impossible. The material in this chapter is divided into three parts:

  1. You’ll get a high level overview of the Ext JS framework.

  2. We’ll do a code review of a new version of the Save Sick Child application developed with Ext JS. This is where we want you to spend most of the time in this chapter. Learn while studying commented code. We’ve also provided multiple links to the relevant product documentation.

  3. If you are developing application where Ext JS (the client) communicates with Java (the server) this part will offer a way to improve your productivity using an open source code generator called Clear Data Builder (CDB).

Why Ext JS?

After learning how jQuery library can simplify development of the HTML5 applications you might be wondering, what’s so good about Ext JS framework that makes it worthwhile for studying. First of all, Core jQuery is just a library of utilities that simplify working with DOM and - you still need to write the Web application using HTML and JavaScript. In addition to it, there are lots and lots of jQuery plugins that include handy widgets to add to your manually created Web site. There are other HTML5 frameworks that help better organizing your project (e.g. Angular.js) or modularizing your project (e.g. Require.js), but enterprise application may need more. So here comes the Ext JS sales pitch.

  1. Ext JS an HTML5 framework that doesn’t require you to write HTML. Yes, your only tiny HTML file will just include three files: one with Ext JS framework, one CSS file, and one app.js, but the <body> section will be empty.

  2. Ext JS offers you a comprehensive JavaScript-based classes that include pretty much everything you need to develop a Web application (UI components, UI layouts, collections, networking, collections, CSS compiler, packaging tool, and more).

  3. Ext JS offers you a way to write object-oriented code, define classes and inheritance in a way that’s closer to classical inheritance and doesn’t require the prototype property.

  4. Ext JS can jump start your application development by generating the initial code layered according to the Model-View-Controller (MVC) design pattern.

  5. Ext JS is a cross-browser framework that promises to automatically take care of all differences in major Web browsers.

If you just finished reading the jQuery chapter, you’ll need to switch to a different state of mind. Core jQuery library was light, it didn’t drastically change the way of developing pure HTML/JavaScript applications. But working with Ext JS framework is a completely different ball game. It’s not about improving an existing Web page, it’s about re-writing it from scratch without using HTML.

Ext JS includes a rich library of UI components, a flexible class system, custom layouts, code generators. But Web browsers understand only HTML, DOM, CSS, and JavaScript. This means that the framework will have to do some extra work in converting the code written using the home-made Ext JS class system into same old HTML objects. Such extra work requires additional time, and we’ll discuss this in the section titled "The Components Lifecycle".

Downloading and Installing Ext JS

First, you need to know that Ext JS framework can be used for free only for non-commercial projects. To use Ext JS for enterprise Web development you or your firm has to purchase one of the Ext JS licenses. But for studying, you can download the complete commercial version of Ext JS for free for the 45-days evaluation period.

Important
The materials presented in this chapters were tested only with the current version of Ext JS framework, which at the time of this writing was 4.2.

After downloading the Ext JS framework, unzip it to any directory of your choice - later on the framework will be copied either in in your project directory (see the Sencha CMD section below) or in the document root of your Web server.

After unzipping the Ext JS distribution, you’ll find a number of files and folders there. There are several JavaScript files containing differently packages Ext JS framework. You’ll need to pick just one of these files. The files that include the word all in their names contain the entire framework and if you’ll include one of the following files, all the classes will be loaded to the user’s browser even though your application may never use most of them.

  • ext-all.js - minimized version of the source code of Ext JS, which literally looks like one line of 1.4 million characters (it’s still JavaScript, of course). Most likely you won’t deploy this file on your production server.

  • ext-all-debug.js - human-readable source code of Ext JS with no comments. If you like to read comments, use ext-all-debug-w-comments.js.

  • ext-all-dev.js - human-readable source code of Ext JS that includes console.log() statements that generates and outputs debugging information in the browser’s console.

Similarly, there are files that don’t include all in their names: ext.js, ext-debug.js, and ext-dev.js. These are much smaller files that do not include the entire framework, but rather a minimum set of classes required to start the application. Later on, the additional classes may be lazy-loaded on the as-needed basis.

The docs folder contains extensive documentation - just open the file index.html in your browser and start reading and studying.

The builds folder includes sandboxed versions of Ext JS in case you need to to use say Ext JS 4.2 along with older versions of this framework. Browsing the builds folder reveals that the Ext JS framework consists of three parts:

  • Ext Core - it’s a free to use JavaScript library for enhancing Web sites. It supports DOM manipulation with CSS selectors, events and AJAX requests. It also offers a syntax to define and create classes that can extend from each other. The functionality of Ext Core is comparable to Core jQuery, but the latter has a huge community of users and contributors, so it’s not likely that anyone would be using just Ext Core.

  • Ext JS - a UI framework that includes a rich library of UI components.

  • The Foundation - a set of useful utilities.

Such code separation allowed creators of Ext JS reuse a large portion of the framework’s code in the mobile library Sencha Touch, which we’ll cover in Chapter 13.

Ext JS framework is large so be prepared that your application will weigh at least 1Mb. So if you just need to create a small Web site, you may be better off by just using a lightweight, easy to learn and free jQuery library or one of a dozen of other JavaScript frameworks that either improve organizational structure of your project or offer a set of a la cart components to prettify your HTML5 application. But if you had a chance to develop or use rich Internet applications developed with such frameworks as Microsoft Silverlight or Apache Flex, then you’ll quickly realize that Ext JS is the closest by functionality, rich set of components and tools.

Getting Familiar with Ext JS and Tooling

This section is not going to be an Ext JS tutorial that gradually explains each and every feature and API of Ext JS. For this we’d need to write a fat Ext JS book. Sencha published multiple online tutorials, documentation and videos, besides there are several current books written by independent authors. In this section chapter you’ll get an overview of the framework.

The First Version of Hello World

But we’ll start with developing a Hello World application before explaining how things work in Ext JS. But the section where we’ll review the code of the Save Sick Child application will serve as a hands-on way of learning the framework. You’ll read the code fragments followed by brief explanations. You’ll be able to run and debug this application on your own computer seeing how various components and program layers work in practice. But first things first - let’s create a couple of versions of Hello World.

Create a new directory (e.g. hello1). Inside hello1 create a subdirectory named ext and copy there the entire content of your Ext JS installation directory. Create yet another subdirectory app inside hello1 - this is where your application JavaScript files will go.

At the very minimum, every Ext JS application will contain one HTML and one JavaScript file - usually index.html and app.js. The file index.html will include the references to the CSS and JavaScript code of Ext JS and will include your app.js containing the code of the Hello World application:

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>HelloWorld</title>
      <link rel="stylesheet" href="ext/resources/ext-all-gray.css">
      <script src="ext/ext.js"></script>
      <script src="app/app.js"></script>
</head>
<body></body>
</html>

The next comes the content of the app.js that you should place in the app directory of your project. This is how the app.js may look like:

Ext.application({
    launch: function(){
      alert("Hello World");
    }
});

This Ext.application() method gets a configuration object - JavaScript literal - with configured launch method that’s called automatically when the Web page has completely loaded. In our case it mandates to launch the anonymous function that displays the "Hello World" message. In Ext JS you’ll be using such configuration objects a lot.

Open the file index.html in your Web browser and you’ll see this greeting. But this was a plain vanilla Hello World. In the next section we’ll automate the process of creation of a fancier Hello World (or the initial version of any other application) by using the Sencha Cmd tool.

In the pre 4.0 versions ofExt JS you’d be invoking the Ext.onReady() method instead of passing the configuration object with the launch config option.

Providing a function argument as a configuration object overrides configurable properties of the current instance of the class. This is different from the class properties, which are defines at the prototype level and changing a values of a property would apply to all instances of the class. When you’ll be reading Ext JS online documentation for any class, you’ll three three categories of class elements: Configs, Properties, and Methods. For example, this is how you can create a panel passing configs:

Ext.create('Ext.panel.Panel', {
    title: 'Hello',
    width: 200,
    html: '<p>World!</p>',
});

In this example we are creating an instance of the panel usinc configuration object with three config options: title, width, and html. The values of these properties will be assigned to the corresponding properties of this instance only. If you’ll read documentation for Ext.panel.Panel, you’ll find 116 available configs that you can set on the panel instance.

Note
Ext JS classes are organized into packages. For example, the class Panel in the above example is located in the package Ext.panel. You’ll be using packaging in your applications too. For example, later in the chapter you’ll see classes from Save Sick Child and Clear frameworks named as SSC.view.DonateForm or Clear.override.ExtJSOverrider. Such packages should be properly namespaced and SSC and Clear are top-level namespaces here. The next fragment shows how to give a name to your application, and such a given name will serve as a top-level namespace.
Ext.application({
    name: 'SSC',
    // more config options go here
});

In the next section we’ll automate the process of creating of Hello World application and will see another example of the configuration object.

Using Sencha CMD Tool

Sencha CMD is a handy command line tool that automates your work starting from scaffolding your application to minimizing, packaging and deploying it.

Download Sencha CMD from http://www.sencha.com/products/sencha-cmd/download. Run the installer, and when it’s complete, open the Terminal or Command window and enter the command sencha - you should see a prompt with all possible commands and options that CMD understands.

For example, to generate the initial project structure for HelloWorld application enter the following command, specifying the absolute path to your ExtJS SDK directory (we keep it in the /Library directory) and to the output folder, where the generated project should reside.

sencha -sdk /Library/ext-4.2 generate app HelloWorld /Users/yfain11/hello

After the code generation was complete, it created the folder hello of the structure shown in CMD-generated project.

image
Figure 1. CMD-generated project

The generated project is created with the assumption that your application will be built using the MVC paradigm discussed in the section Best Practice:MVC. The JavaScript is located in the app folder, which includes the view subfolder with the visual portion of your application, the controller folder with controller classes, and the model is for data. The ext folder contains multiple distributions of the Ext JS framework. The sass folder is a place for your application’s CSS files (see the sidebar titled SASS and CSS later in this chapter).

The entry point to your application is index.html, which contains the references to the main application file app.js, the Ext JS framework extdev-js, the CSS file bootstrap.css (imports the classic theme), and the supporting script bootstrap.js, which contains the mapping of the long names if the framework and application classes to their shorter names (xtypes).

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>HelloWorld</title>
    <!-- <x-compile> -->
        <!-- <x-bootstrap> -->
            <link rel="stylesheet" href="bootstrap.css">
            <script src="ext/ext-dev.js"></script>
            <script src="bootstrap.js"></script>
        <!-- </x-bootstrap> -->
        <script src="app/app.js"></script>
    <!-- </x-compile> -->
</head>
<body></body>
</html>

The content of the generated app.js is shown next. This script just calls the method Ext.application() passing as an argument configuration object that specifies the application name, and the names of the classes that play roles of views and controller. We’ll go into details a bit later, but at this point let’s concentrate on the big picture.

Ext.application({
    name: 'HelloWorld',

    views: [
        'Main',
        'Viewport'
    ],

    controllers: [
        'Main'
    ],

    autoCreateViewport: true
});

Finally, if you’ll open index.html in your Web browser, you’ll see our Hello World initial Web page that looks as in [Fig6-2]. This view uses so called border layout and shows a panel on the west and a tabpanel in the central region of the view.

image
Figure 2. Running the Generated Application

The total size of this version of the Hello World application is pretty large: 4Mb, and the browser makes 173 requests to the server by the time the user sees the application shown on Running the Generated Application. But Sencha Cmd knows how to build the production version of the Ext JS application. It minimizes and merges the application’s and required framework’s JavaScript code into one file. The application css file is also minimized and the references to the image resources become relative hence shorter. Besides, the images may be automatically sliced - cut into smaller rectangular pieces that can be downloaded by the browser simultaneously.

To create optimized version of your application go to the Terminal or a command window and change to the root directory of your application (in our case it’s /Users/yfain11/hello) and run the following command:

sencha app build

After the build is finished, you’ll see newly generated version of the application in the directory build/HelloWorld/production. Open the file index.html while running Chrome Developers Tools, and you’ll see that the total size of the application is substantially lower (about 900Kb) and the the browser had to make only five requests to the server (see Running production version of HelloWorld). Using gZip will reduce the size of this application to 600Kb, which is still a lot, but Ext JS framework is not the right choice for writing Hello World type of applications or light Web sites.

image
Figure 3. Running production version of HelloWorld
Note
With older versions of Sencha Cmd (Sencha SDK Tools) you would generate a manifest file in a jsb3 format, that would describe all the classes that your application uses. Such jsb3 file would be used for creating custom builds. This option is still available, but Sencha Cmd uses the command sencha app build as was shown above. For more details about code generation refer to the section Using Sencha Cmd with Ext JS in the product documentation.
Tip
Sencha Desktop Packager allows you to take an existing Ext JS Web application (or any other HTML5 application) and package it as a native desktop application for Windows and MAC OS X.

Later in this chapter we’ll use Sencha CMD tool again in the section "Building Production Version" to create an optimized version of the Save Sick Child application.

Important
If your organization is developing Web applications with Ext JS without using Sencha CMD - it’s a mistake. Sencha CMD is a very useful code generator and optimizer that also enforces the MVC principles of application design.

Configuring Eclipse IDE, Apache Tomcat, and Ext JS

In this chapter we’ll switch from Aptana IDE to its parent Eclipse IDE. Although Eclipse is not the best IDE for JavaScript developers, but it’s the most popular IDE among enterprise Java developers, and we’ll need it later in this chapter to demonstrate the application generation using Java and CDB. Besides, Sencha offers Eclipse plugin (not covered in the book) for those who purchased a license of Sencha Complete .

We’ll use two IDEs in this chapter: Eclipse and WebStorm. In particular, we’ll use the version "Eclipse IDE for Java EE developers", which is the most popular IDE among enterprise Java developers. It’s available free of charge at Eclipse Downloads site. The installation comes down to unzipping of the downloaded archive. Then double-click on the Eclipse executable, and you’ll see the workbench that looks very similar to Aptana.

But in our opinion, the best IDE for JavaScript developers (regardless of which framework they use) is JetBrain’s WebStorm IDE. Later in this chapter we’ll show you how to use WebStorm for developing JavaScript portion of the project even if the Java part has to be developed in Eclipse.

Apache Tomcat

Apache Tomcat is probably the most popular free and open source server used by Java developers for deploying Web applications. Besides being a Web Server, Tomcat also contains Java Servlet container that will be used later in this chapter in the section "Generating CRUD applications". But for most examples we’ll use Tomcat as a Web server where Ext JS code will be deployed.

Get the latest version of Apache Tomcat from the Download section at http://tomcat.apache.org. At the time of this writing Tomcat 7.0.39 is the latest build, so download the zip file with the Tomcat’s Binary Distributions (Core). Unzip the file in the directory of your choice.

Even though you can start Tomcat from a separate command window, the more productive way is to configure Tomcat right in the Eclipse IDE. This will allow to deploy your applications, and start/stop Tomcat without the need to leave Eclipse.

To add a server to Eclipse, open Eclipse Java EE perspective (menu Window | Open Perspective ), select the menu File | New | Other | Server |Server | Apache | Tomcat v7.0 Server, select your Tomcat installation directory and press Finish. If you don’t see Tomcat 7 in the list of Apache servers, click on “Download additional server adapters”.

You’ll see the Tomcat entry in the Eclipse Project Explorer. Go to Eclipse menu Windows | Show View and open the Servers view. Start Tomcat using the right-click menu.

Tip
By default, Eclipse IDE keeps all required server configuration and deployment files in its own hidden directory. To see where exactly they are located in your computer, just double-click on the name of Tomcat in the Server view. The server path field contains the path. Keep in mind that while Tomcat documentation defines webapps as a default deployment directory, Eclipse uses wtpwebapps directory instead. If you prefer to deploy your Eclipse projects under your original Tomcat installation path, select the option Use Tomcat Installation.

In the next section you’ll learn how to create Dynamic Web Projects in Eclipse, where you’ll need to specify the Target Runtime for deployment of your Web applications. This newly installed and configured Tomcat server will serve as a deployment target for our sample projects.

Dynamic Web Projects and Ext JS

Eclipse for Java EE developers comes with Web Tools Platform that simplifies development of Web applications by allowing you to create so-called Dynamic Web Project, which will be specifically created for deployment under a particular Java server - Apache Tomcat in our case.

To create such a project select Eclipse menu File | New | Other | Web | Dynamic Web Project. It’ll pop up a window similar to Creating Dynamic Web Project in Eclipse. Note that the Target Runtime is Apache Tomcat v7.0 that we’ve configured in the previous section.

image
Figure 4. Creating Dynamic Web Project in Eclipse

Upon creation, this project will include several directories, and one of them will be called WebContent. This directory it serves as a document root of the Web server in Eclipse Dymamic Web Projects . This is the place to put your index.html and one of possible places to keep the Ext JS framework.Create a subdirectory ext under WebContent and copy there all files from the Ext JS distribution. The app directory should also go under WebContent.

Unfortunately, Eclipse IDE is infamous for slow indexing of JavaScript files, and given the fact that Ext JS has hundreds of JavaScript files, your work may be interrupted by Eclipse trying to unnecessary re-validate these files. Developers of Sencha Eclipse plugin decided to solve this problem by creating a special type library file (ext.ser) supporting code assistance in Eclipse. This solution will work until some of the Ext JS API changes, after that Sencha should update the type library file.

If you don’t have Sencha Eclipse plugin, there is a couple of solutions to this problem (we’ll use the first one).

  1. Exclude from Eclipse build the following Ext JS directories: ext, build, and packages.

  2. Don’t copy the Ext JS framework into your Eclipse project. Keep it in the place known for Tomcat, and configure as a loadable module.

To implement the first solution, right click on the properties of your project and select JavaScript | Include Path. Then switch to the Source tab, expand the project’s Web content and press the buttons Edit and then Add. One by one add the ext, build, and packages as exclusion patterns (add the slash at the end) as shown in [Fig6-5]

image
Figure 5. Excluding folders in Eclipse

For the second solution, you’ll need to add your Ext JS folder as a static Tomcat module. Double-click at the Tomcat name in the Servers view and then click on the bottom tab Modules. Then Click on Add External Web Module. In the popup window find the folder where your Ext JS is (in my computer it’s inside the Library folder as in [Fig6-6]) and give it a name (e.g. /extjs-4.2). Now Tomcat will know that on each start it has to load year another static Web module known as /extjs-4.2. If you’re interested in details of such deployment, open up the file server.xml located in your Eclipse workspace in the hidden directory .metadata/.plugins/org.eclipse.wst.server.core/tmp0/conf.

To ensure that you did everything right, just enter in your browser the URL http://localhost:8080/extjs-4.2, and you should see the welcome screen of Ext JS.

image
Figure 6. Adding Ext JS to Tomcat as a static module

In both of these solutions you’ll lose the Ext JS context sensitive help, but at least you will eliminate the long pauses caused by Eclipse internal indexing processes. Again, developing ExtJS code in WebStorm IDE or IntelliJ IDEA IDEs would spare you from all these issues because these IDE’s are smart enough to produce context-sensitive help from an external JavaScript library.

In this section we brought together three pieces of software: Eclipse IDE, Apache Tomcat server, and Ext JS framework. Let’s bring one more program to the mix: Sencha CMD. We already went through the initial code generation of Ext JS applications. If you already have a Dynamic Web Project in Eclipse workspace, run Sencha CMD specifying the WebContent directory of your project as the output folder, where the generated project will reside. For example, if the name of your Dynamic Web Project is hello2, the Sencha CMD command can look as follows:

sencha -sdk /Library/ext-4.2 generate app HelloWorld /Users/yfain11/myEclipseWorkspace/hello2/WebContent

Which Ext JS Framework to use?

First you need to select the packaging of the Ext JS framework that fits your need. You may select its minimized version to be used in production or a larger and commented version with detailed comments and error messages. Besides, you may select a version of Ext JS that include either all or only the core classes. The third option is to create a custom build of Ext JS that will include only the those framework classes that are used by your application.

The file with the minimized production version of Ext JS is called ext-all.js and its size is pretty large: 1.4Mb. But as we described in the section on Sencha CMD, your can create a build fine tuned to your application . If this application will be used on the high-speed networks and size is not the object, simply add it to your index.html from your local servers or see if Sencha offers the CDN for the Ext JS version you need, which may look similar to the following:

<link rel="http://cdn.sencha.io/ext-4.2.0-gpl/resources/css/ext-all.css" />

<script type="text/javascript" charset="utf-8"
        src="http://cdn.sencha.io/ext-4.2.0-gpl/ext-all.js"></script>
Tip
If the name of the file with Ext JS framework contains -all, this indicates that all framework classes are included in this file.

Declaring, Loading and Instantiating Classes

In Chapter 1 we’ve stated that JavaScript doesn’t have classes and that constructor functions were the closest to classes language elements. Ext JS extends the JavaScript language and introduces classes and a special way to define and instantiate them with functions Ext.define() and Ext.create(). Ext JS also allows to extend one class from another using the property extend and define class constructors using the property constructor.

With Ext.define() contains you declare a class declaration, and Ext.create() instantiate it. Basically, define() serves as a template for creation of one or more instances.

Usually the first argument you specify to define() is a fully qualified class name, the second argument is an object literal that contains the class definition. If you use null as the first argument Ext JS creates an anonymous class.

The next class Header has 200 pixel height, uses the hbox layout, has a custom config property logo, extends Ext.panel.Panel:

Ext.define("SSC.view.Header", {
  extend: 'Ext.panel.Panel',

  title: 'Test',
  height: 200,
  renderTo: 'content',        // (1)

  config: {
     logo: 'sony_main.png'    //  (2)
  },

  layout: {
    type: 'hbox',
    align: 'middle'
  },

  constructor: function() {     // (3)
    this.callParent(arguments);
 }
});
  1. Render this panel to an HTML element with id=content.

  2. Defining a custom config property logo.

  3. This is a constructor function. In this example it calls the constructor of its superclass Ext.panel.Panel. In general, it’s a good practice to pass the standard JavaScript array arguments in one shot rather than trying to explicitly define each and every parameter.

You can optionally include a third argument for define(), which is a function to be called when the class definition is created. Now you can create one or more instances of the class class, for example:

var myHeader = Ext.create("SSC.view.Header");

The values of custom config properties from the config{} section of the class can be reassigned during the class instantiation. For example, the next code snippet will print sony.png for the first instance of the header, and sony_small.png for the second one. Please not that Ext JS automatically generated getters and setters for all config properties, which allowed us to use the method getLogo().

Ext.onReady(function () {
   var myHeader1 = Ext.create("SSC.view.Header");
   var myHeader2 = Ext.create("SSC.view.Header",
                                { logo: 'sony_small.png' });

    console.log(myHeader1.getLogo());
    console.log(myHeader2.getLogo());
});
Tip
Don’t forget about an online tool JSFiddle that allows you to test and share JavaScript code snippets.JSFiddle knows about Ext JS 4.2 already. For example you can run the code snippet above by following this JSFiddle link. If it doesn’t render the styles properly, check the URL of the ext-all.css in the section External Resources.

If a class has dependencies on other classes, which must be preloaded, use the requires parameter. For example, the next code snippet shows that the class SSC.view.Viewport requires the Panel and the Column classes. So the Ext JS loader will check if Panel and/or Column was not loaded yet, it’ll dynamically load them first.

Ext.define('SSC.view.Viewport', {
    extend: 'Ext.container.Viewport',
    requires: [
        'Ext.tab.Panel',
        'Ext.layout.container.Column'
    ]
    // the rest of the class definition is omitted
});

Ext.create() is a preferred way of instantiation as it does more than the new operator that is also allowed in Ext JS. But Ext.create() may perform some additional functionality, for example id Ext.Loader is enabled, create() will attempt to synchronously load dependencies (if you haven’t used the option require). But with requires your preloads all dependencies asynchronously in parallel and is a preferred way of specifying dependencies. Besides, the async mode allows loading from different domains, while sync loading doesn’t.

Dynamic Class Loading

The singleton Ext.Loader offers a powerful mechanism of dynamic loading of any classes on demand. You have to explicitly enable the loader first thing after including the Ext JS framework in your HTML file it providing the paths where the loaded should look for files, for example

<script type="text/javascript">
    Ext.Loader.setConfig({
      enabled: true,
      disablrCaching: false,
      paths: {
          'SSC': 'my_app_path'
      }
    });
</script>

Then the manual loading of a class can be done using Ext.require('SSC.SomeClass') or Ext.syncRequire('SSC.SomeClass').

For each class Ext JS creates one instance of special class Ext.Class, which will be shared by all objects instantiated from this class.

Tip
The instance of any object has access to its class via a special variable self.

Prior to creating a class, Ext JS will run some pre-processors and some post-processors based on the class definition. For example, the class 'SSC.view.Viewport' from the code sample above uses extend: 'Ext.container.Viewport', which will engage the extend pre-processor that will do some background work to properly build a subclass of extend: Viewport. If your class includes the config section, the config preprocessor will be engaged.

What’s xtype

One of the interesting pre-processors is xtype, which allows to skip the invocation of the create() method for creating the instance of the class. Every Ext JS component has assigned xtype. For example, Ext.panel.Panel has an xtype of panel. Online documentation displays the name of the corresponding xtype in the header of each component as in Each component has an xtype.

image
Figure 7. Each component has an xtype

If you are defining a class and include its xtype, Ext JS will instantiate if for you without the need to call create(). You can find many examples of using the xtype property in the section "Developing Save Sick Child with Ext JS" later in this chapter. For example, the following class definition includes many components with the xtype property.

Ext.define("SSC.view.LoginBox", {
    extend: 'Ext.Container',
    xtype: 'loginbox',

    layout: 'hbox',

    items: [{
        xtype: 'container',
        flex: 1
    }, {
        xtype: 'textfield',
        emptyText: 'username',
        name: 'username',
        hidden: true
    }, {
        xtype: 'textfield',
        emptyText: 'password',
        inputType: 'password',
        name: 'password',
        hidden: true
    }, {
        xtype: 'button',
        text: 'Login',
        action: 'login'
    }]
});

Most of the above components use the standard Ext JS xtype values, so the fact that you have included them into the class SSC.view.LoginBox is a command for Ext JS to instantiate all these buttons and text fields. But the class SSC.view.LoginBox also includes xtype: 'loginbox' - we decided to assign the value loginbox to serve as the xtype of our class. Now, you can use the statement xtype: 'loginbox' in any other container, it’ll know how to instantiate it. For example, later in this chapter you’ll see the complete code of the main window SSC.view.ViewPort, which includes (and instantiates) our login box as follows:

   items: [{
       xtype: 'loginbox',
       margin: '10 0 0 0'
   },
   // more items go here
   ]
Tip
Ext JS UI components allows you to use alias, which have similar to xtype use. Each of the aliases have to have a widget prefix, e.g. alias: 'widget.DonatePanel' in a definition of the component that extends Ext.panel.Panel.
Ext JS Mixins

Object-oriented languages Java and C# can be considered as simpler version of C. One of the C features that didn’t make it into Java and C# was support of multiple inheritance: a class can extend only one other class. This was done for a good reason - debugging of the C++ programs that were written with multiple inheritance was difficult.

Ext JS supports multiple inheritance via JavaScript mixins. A class constructor can get any object as an argument, and Ext JS will use its property values to initialize the corresponding properties defined in the class, if they exist, and the rest of the properties will be created on the fly. The following code snippet shows how to define a classB that will have features defined in classes classA,classC, and classD.

Ext.define("MyApp.classB",{
  extend: "MyApp.classA",
  mixins: {classC: "MyApp.ClassC"
           classD, "MyApp.classD"}

  }
  ...
});

WARNING: If more than one mixin has a method with the same name, the first method that was applied to the resulting class wins. To avoid collisions Ext JS allows you to provide fully qualified name of the method, for example `this.mixins.classC.conflictingName(); this.mixins.classD.conflictingName();`.

MVC in Ext JS

While Ext JS doesn’t force you to architect your application based on the MVC paradigm, it’s a really good idea to do so. Earlier in the section on Sencha CMD you’ve seen how this tool generates a project, which separates model, views, controllers and stores into separate directories as in CMD-generated project that depicted the structure of the Hello World project. But later in this chapter we’ll build our Save Sick Child application the same way. Model-View-Controller in Ext JS presents a diagram illustration how the Ext JS application that contains all Model-View-Controller tiers.

image
Figure 8. Model-View-Controller in Ext JS
  • Controller is an object that serves as an intermediary between the data and the views. The data has arrived to your application, and controller has to notify the appropriate view. The user changed the data on the view - the controller should pass the changes to the model (or stores in the Ext JS world). Controller is the place to write event listeners reaction to some important events of your application (e.g. a user clicked on the button). In other words, Controller maps the events to actions to be performed on the data or the view.

  • View is a certain portion of the UI that the user sees. The view is populated with the data from the model (or stores).

  • Model represents some business entity, e.g. Donor, Campaign, Customer, Order e.t.c. In Ext JS models are access via stores.

  • Store contains one or more model instances. Typically, a Model is a separate class that is instantiated by the store object, but in simple cases a store can have the model data embedded in its own class. A store may use more than one model if need be. Both stores and model can communicate with the data feed that in a Web application is usually provided by some server-side data feed.

The application object defines its controllers, views, models, and stores. When the Save Sick Child will be ready, the code of its app.js will look as follows:

Ext.application({
    name: 'SSC',

    views: [
        'CampaignsMap',
        'DonateForm',
        'DonorsPanel',
        'Header',
        'LoginBox',
        'VideoPanel',
        'Viewport'
    ],

    stores: [
        'Campaigns',
        'Donors'
    ],

    controllers: [
        'Donate'
    ]
});

The above code is a clean and simple to read/write code helps Ext JS framework in generating additional code required for wiring views, models, controllers and stores together. For better understanding of the rest of this chapter you should read the MVC Architecture section from Ext JS documentation. We don’t want to repeat the content of Sencha product documentation, but rather will be giving you brief descriptions while doing code review of the Save Sick Child application.

Models and Stores

When you create a class to be served as a model, it must be a subclass of Ext.data.Model. A Model has the fields property. For example, this is how the you can represent a Donor entity using just two fields: name and location:

Ext.define('HR.model.Donor',{
    extend: 'Ext.data.Model',
    requires: [
        'Ext.data.Types'
    ],

    fields: [
        { name: 'donors',   type: Ext.data.Types.INT },
        { name: 'location', type: Ext.data.Types.STRING}
    ]
});

Think of an instance of a model is a of one record representing some business entity, e.g. Donor. Ext JS generate getters and setters for models, so if an instance of the model is represented by a variable sscDonor, you can set or get its value as follows:

sscDonor.set('name', 'Farata Systems');
var donorName= sscDonor.get('name');

A store in Ext JS holds a collection of instances of some model. For example, if you the application has retrieved the information about ten donors, it’ll be represented in Ext JS as a collection of yen instances of the class Donor. A custom store in your application has to extend from the class Ext.data.Store.

If you need to quickly create a mock store for testing purposes, you can declare a store with inline data that you can specify using the config option data. The next code sample shows a declaration of the store for providing the information about the donors as inline data:

Ext.define('SSC.store.Donors', {
    extend: 'Ext.data.Store',

    fields: [
        { name: 'donors',   type: 'int' },
        { name: 'location', type: 'string' }
    ],

    data: [
        { donors: 48, location: 'Chicago, IL' },
        { donors: 60, location: 'New York, NY' },
        { donors: 90, location: 'Dallas, TX' }
    ]
});

It’s a good idea to have a mock store with the test data located right on your computer. This way you won’t depend on the readiness and availability of the server-side data. But usually, a store makes some AJAX call to a server and retrieves the data via the object Ext.data.reader.Reader or one of its descendants, for example:

Ext.define('SSC.store.Donors', {
    extend: 'Ext.data.Store',

    model: 'SSC.model.Donor',           // (1)
    proxy: {                  // (2)
        type: 'ajax',
        url: 'donors.json',   // (3)
        reader: {             // (4)
           type: 'json'
        }
    }
});
  1. The model SSC.model.Donor has to be described in your application as a separate class and contain only the fields defined, no data.

  2. Unless you need to load some raw data from a third-party server provider, wrap your reader into a Proxy object. Server proxies are used for implementing CRUD operations and include the corresponding methods - create(), read(), update(), destroy().

  3. The name of the json-formated data file that contains an array of object literals (each object represents one donor).

  4. The Reader object will consume JSON. Read the Ext JS documentation to decide how to properly configure your JSON reader. The reader knows how to convert the data into the model.

Populating of a store with the external data is usually done via a Proxy object, and Ext JS offers several server side proxies: Ajax, JsonP, Rest, and Direct. To retrieve the data from the server you’d be calling the method load() on your Store object. To send the data to the server - call the method sync().

The most frequently used proxy is Ajax, which uses XMLHttpRequest to communicate with the server. The code fragment below shows another way of defining the store Donors. It specifies via the config api the server sides URIs responsible for the four CRUD operations. We’ve omitted the reader section here because the default data type is JSON anyway.

Ext.define('SSC.store.Donors', {
    extend: 'Ext.data.Store',

    model: 'SSC.model.Donor',
    proxy: {
        type: 'ajax',
        url: 'donors.json',
        api: {
           create: '/create_donors',
           read: '/read_donors',
           update: '/update_donors',
           destroy: '/destroy_donors',
        }
    }
});

When you create an instance of the data store you can specify the autoload parameter. It it’s true, the store will be populated the store automatically. Otherwise, explicitly call the method load() whenever the data retrieval is needed. For example, you can call the method myStore.load({callback:someCallback}) passing it some callback to be executed.

Tip
In Chapter 1 we were discussing HTML5 local storage API. Ext JS has a class Ext.data.proxy.LocalStorage that saves the model data locally if the Web browser supports it.
Controllers and Views

Your application controller is a liaison between the data and the views. This class has to extend Ext.app.Controller, and will include references to the views and, possibly stores. Controller will automatically load every class mentioned in its code, create an instance of each store and register each instance with the class [Ext.StoreManager].

A controller class has config properties stores, models, and views, where you can list stores, models, and views that controller should know about. For example, the next code listing shows the controller SSC.controller.Donate includes the names of two stores - SSC.store.Campaigns and SSC.store.Donors.

Ext.define('SSC.controller.Donate', {
    extend: 'Ext.app.Controller',
    stores: ['SSC.store.Campaigns', 'SSC.store.Donors']  // (1)

    refs: [{                                      // (2)
        ref: 'donatePanel',
        selector: '[cls=donate-panel]'
        // more views go here
    }],

    init: function () {                            // (3)

        this.control({
            'button[action=showform]': {
                click: this.showDonateForm
            }
            // more event listeners go here
        });
    },

    showDonateForm: function () {                  // (4)
        this.getDonatePanel().getLayout().setActiveItem(1);
    }
});
  1. Listing stores in your controller. Actually, in most cases you’d list stores is the Ext.application singleton as we did earlier. But if you need to dynamically create controllers, you don’t have a choice but declare stores in such controllers.

  2. Listing one or more views of your application in the refs property, which simplifies the search of the component globally or within some container.

  3. Registering event listeners in the function init()

  4. An event handler to process clicks on the button that has an attribute action=showform

Ext.StoreManager provides a convenience method to look up the store by store ID. If stores were automatically injected into Ext.StoreManager by the controller, the default store ID is its name, e.g. SSC.store.Donors:

var donorsStore = Ext.data.StoreManager.lookup('SSC.store.Donors');

// An alternative syntax to use StoreManager lookup
var donorsStore = Ext.getStore('SSC.store.Donors');

The above SSC.controller.Donate doesn’t use the config properties views, but it it did, Ext JS would generate getters and setters for every view (the same is true for stores and models). It uses refs instead to reference components, and getters and setters will be generated for each components listed in refs, e.g. getDonatePanel(). Lookup of such components is done based on the value in selector using the syntax compatible with ComponentQuery.

Tip
You can view and test Ext JS components against bundled themes browsing the Theme Viewer at the Ext JS 4.2 Examples page.

Component’s Lifecycle

In previous versions of our Save Sick Chils application CSS was responsible for all layouts of the UI components. In Chapter 11 you’ll be learning about the responsive design techniques and CSS media queries, which allow to create fluid layouts that automatically adjust to the size of the viewport.

But this section is about Ext JS proprietary way of creating and adding UI components to Web pages. Before the user will see a component, Ext JS framework will go through the following phases for each component:

  • Load - load the required (or all) Ext JS classes and their dependencies

  • Initialize components when the DOM is ready

  • Rendering - convert components to HTML elements

  • Layout - measuring and assigning sizes

  • Destruction - removing the reference from DOM, removing event listeners and unregistering from the component manager.

Rendering and layout are the most time consuming phases. The rendering does a lot of preparations to give the browser’s rendering engine HTML elements and not Ext JS classes. The layout phase is slow because the calculation of sizes and positions (unless they are in absolute coordinates) and applying of cascading stylesheets takes time. Ext JS 4.1 was redesigned to minimize the number of reflows, which happen when the code reads-measures-writes to the DOM and makes dynamic style modifications. Now a large portion or recalculations is done in a batch before modifying the DOM.

Each Web page consists of one or more containers, which include some children - components (in Ext JS they are subclasses of Ext.Component), for example, Ext.button.Button. If a component can contain other components, it’s a container (e.g. Ext.panel.Panel) and will have Ext.container.Container as one of its ancestors. In Ext JS class hierarchy, Container is a subclass of Component, so all methods and properties defined for a component are available for a container too.

You’ll be defining your container class with as a subclass of a container by including extend: Ext.container.Container. The child elements of a container are accessible via its property items. In the Ext.define() statement of the container you may specify the code that will loop through this items array and, say style the components, but actual instances of the children will be provided during the Ext.create() call via configuration object.

The process of adding a component to a container will typically consist of invoking Ext.create() and specifying in a configuration object where to render the component to, for example renderTo: Ext.getBody().

But under the hood Ext JS will do a lot more work. The framework will auto-generate a unique ID for the component, assign some event listeners, instantiate component plugins if specified, invoke the initComponent(), and add the component to Ext.ComponentManager.

Warning
Even though you can manually assign an ID to the component via configuration object, it’s not recommended to avoid duplicate IDs.
Tip
At any time your program can get from the ComponentManager a reference to a component by ID, for example, var donationForma = Ext.getCmp();

Events

Events in Ext JS are defined in the mixin Ext.util.Observable. Components interested in receiving events can subscribe to them using on of the following methods:

  • By calling the method addListener()

  • By using the method on().

  • Declaratively

The next code snippet shows two different ways of how a combobox can subscribe to the event change. The handler function is a callback that will be invoked if the event change will be dispatched on this combobox:

combobox.addListener('change', myEventHandlerFunction);

combobox.on('change', myEventHandlerFunction);

To unsubscribe from the event call the method removeListener() or its shorter version un():

combobox.removeListener('change', myEventHandlerFunction);
combobox.un('change', myEventHandlerFunction);

You can also declarativly subscribe to events using listeners property of the component:

Ext.create('Ext.button.Button', {
   listeners: {
       click: function() { // handle event here }
   }
}

In Chapter 1 you’ve learned about event bubbling. In Ext JS event bubbling mechanism enables events dispatched by components that include Ext.util.Observable bubble up through all enclosing containers. For components it means that you can handle component’s event on container level. It can be handy to subscribe and handle multiple similar events in one place. To enable bubbling for selected events use the enableBubble() method, for example:

this.enableBubble(['textchange', 'validitychange']);

To define custom events use the method addEvents(), where you can provide one or more of the custom event names:

this.addEvents('messagesent', 'updatecompleted');

For components you have to define custom events inside the initComponent() method. For controllers - inside init(), and for any other class – inside its constructor.

Layouts

In Ext JS each container has a property items, which stores references to its child components. The container’s property layout controls how its children are laid out inside. If you won’t explicitly set the layout property, its default value is Auto, which is just placing components inside the container top to bottom regardless of the component size.

Usually you’ll be explicitly specifying the layout. For example, the hbox layout would arrange all components inside the container horizontally next to each other, but vbox layout would arrange them vertically. The card layout places the components one under another, but only the top component is visible (think of a tab folder, where the content of only one tab is visible at any given time).

The border layout is often used to arrange the components in the main viewport (a.k.a. home page) of your application. This layout allows you to split the container’s real estate into five imaginary regions: north, east, west, south, and center. If you need to allocate the top menu items, place them to the region north. The footer of the page is in the south as shown in the code sample below.

Ext.define('MyApp.view.Viewport', {
  extend: 'Ext.container.Viewport',

  layout: 'border',

  items: [{
    width: 980,
    height: 200,
    title: "Top Menu",
    region: "north",
    xtype:  "panel"},
   {
    width: 980,
    height: 600,
    title: "Page Content",
    region: "center",
    xtype:  "panel"},
   },
   {
    width: 980,
    height: 100,
    title: "The footer",
    region: "south",
    xtype:  "panel"},
   }]
});
The flex Property

Ext JS has a property flex that allows make your layout more flexible. Instead of specifying the width or height of a child component in absolute values you can split the available space proportionally. For example if the space has to be divided between two components having the flex values 2 and 1, this means that the 2/3 of the container’s space will be allocated to the first component, and 1/3 to the second one as illustrated in the following code snippet.

 layout: 'vbox',

 items: [{
   xtype: 'component',
   html: 'Lorem ipsum dolor',
   flex: 2
   },
   {
   xtype: 'button',
   action: 'showform',
   text: 'DONATE NOW',
   flex: 1
 }]

Tip: the format of this book doesn’t allow us to include detailed description of major Ext JS component. If you are planning to use Ext JS for development of enterprise Web applications, allocate some extra time to learn the data grid Ext.grid.Panel that’s used for rendering of tabular data. You should also master working with forms with Ext.form.Panel.

In the next section you’ll see Ext JS layouts in action while working on the Save Sick Child application.

Developing Save Sick Child With Ext JS

In this section we’ll do a code walk-through of the Ext JS version of our Save Sick Child application. We’ve prepared two separate Eclipse projects SSC_Top_ExJS.zip contains the top portion of the application, and SSC_Complete_ExtJS.zip contains the complete version. To test these applications in Eclipse, you need to have it configured with Tomcat as described earlier in the section "Configuring Eclipse project with Apache Tomcat and Ext JS".

Save Sick Child: The Top Portion

Then select Eclipse menu File | Import | General | Existing projects into workspace and press the button Next. Then select the option Select archive file and press browse to find SSC_Top_ExJS.zip on your disk. This will import the entire Dynamic Web Project, and most likely you’ll see one error in the Problems view indicating that the target runtime with so-and-so name is not defined. This may happen because the name of the Tomcat configuration in your Eclipse is different from the one in the file SSC_Top_ExJS.zip.

To fix this issue right-click on the project name and select the menu Properties | Targeted runtimes. Then uncheck the Tomcat name that was imported from our archive and check the name of your Tomcat configuration. This action will make the project SSC_Top_ExtJS deployable under your Tomcat server. Right-click on the server name in the Servers view and select Add and Remove menu item. You’ll see a popup window similar to Deploying Dynamic Web Project, which depicts a state when the project SSC_Top_ExtJS is deployed, but SSC_Complete_ExtJS.

image
Figure 9. Deploying Dynamic Web Project

Right-click on the project name SSC_Top_ExtJS, select the menu Run as | Run on server. Eclipse may offer to restart the server - accept it, and you’ll see the top portion of the Save Sick Child application running in the internal browser of Eclipse as shown on Running the SSC_Top_ExtJS in Eclipse. You can either configure Eclipse to use your system browser or just enter the URL http://localhost:8080/SSC_Top_ExtJS/ in the browser of your choice - the Web page will look the same.

image
Figure 10. Running the SSC_Top_ExtJS in Eclipse
Tip
Apache Tomcat runs on the port 8080 by default. If you want to change the port number, double-click on the Tomcat name in the Servers view and change the port there.

It’s time for a code review. The initial application was generated by Sencha CMD so the directory structure complies with the MVC paradigm. This version has one controller Donate.js and three views: DonateForm.js, Viewport.js, and Header.js as shown in Controller, views and images of SSC_Top_ExtJS. The images are located under the folder resources.

image
Figure 11. Controller, views and images of SSC_Top_ExtJS
Using two IDEs: WebStorm and Eclipse

If you prefer using WebStorm for JavaScript development, but have to use Eclipse for some other reasons, you can create a project in WebStorm pointing at the WebContent directory of your Eclipse project. This way you’ll be enjoying a very smart context sensitive help offered by WebStorm, and all code modifications become immediately visible in the Eclipse project.

To open the content of Eclipse WebContent directory in WebStorm select its menu File | Open Directory and point it at the WebContent directory of your Eclipse project.

Mac users can also do it another way:

  1. Create a script to launch WebStorm from the command line. To do this start Storm and open its menu Tools | Create Launcher Script. Agree with defaults offered by the popup window shown in Creating the launch script for WebStorm or select other directory located in the PATH system variable of your computer. This will create a script named wstorm there, and you’ll be able to start WebStorm from a command line.

    image
    Figure 12. Creating the launch script for WebStorm
  2. Open a Terminal window and switch to the directory WebStorm of your Eclipse project. Type there the command wstorm ., and it’ll open the WebStorm with the entire content of your WebContent project. So all JavaScript development you’ll be doing in WebStorm, and the Java-related coding in Eclipse while using the same WebContent directory.

Such setup looks like an overkill, but we are talking about the enterprise development where you may jump through some hoops to create a convenient working environment for yourself.

Tip
To make WebStorm work faster, exclude directories ext, packages, build, and WEB-INF from the project (hit the icon with a wrench image on the toolbar and select the Directories and the Excluded). This way WebStorm won’t be indexing these directories.

The app.js is pretty short - it just declares SSC as the application name, views and controllers. By adding the property autoCreateViewport: true we requested the application to automatically load the main window, which must be called Viewport.js and located in the view directory.

Ext.application({
    name: 'SSC',

    views: [
        'DonateForm',
        'Header',
        'Viewport'
    ],

    controllers: [
        'Donate'
    ],

    autoCreateViewport: true
});

In this version of the application controller Donate.js is listening to the events from the view DonateForm. It’s responsible just for the showing and hiding the Donate form panel. We’ve implemented the same behavior as in the previous version of the SaveSick Child application - click on the Donate Now button reveals the donation form. If the application would need to make some AJAX calls to the server, such code would also be placed in the controller. The code of the Donate controller looks as follows:

Ext.define('SSC.controller.Donate', {
  extend: 'Ext.app.Controller',

  refs: [{
    ref: 'donatePanel',
    selector: '[cls=donate-panel]'
  }],

  init: function () {                 // (1)
    'use strict';

    this.control({
      'button[action=showform]': {    // (2)
        click: this.showDonateForm
      },

      'button[action=hideform]': {
        click: this.hideDonateForm
      },

      'button[action=donate]': {
        click: this.submitDonateForm
      }
    });
  },

  showDonateForm: function () {        // (3)
    this.getDonatePanel().getLayout().setActiveItem(1); // (4)
  },

  hideDonateForm: function () {
    this.getDonatePanel().getLayout().setActiveItem(0);
  },

  submitDonateForm: function () {
    var form = this.getDonatePanel().down('form'); // (5)
    form.isValid();
  }
});
  1. The init() method is invoked only once on instantiation of the controller.

  2. The control() method of the controller takes selectors as arguments to find components with the corresponding event listeners to be added. For example, 'button[action=showform]' means "find a button that has a property action with the value `showform`" - it has the same meaning as in CSS selectors.

  3. Event handler functions to process show, hide, and submit events.

  4. In containers with card layout, you can make one of the components visible (the top one in the card deck) by passing its index to the method setActiveItem(). The Viewport.js includes a container with the card layout (see ` cls: 'donate-panel'` in the next code sample).

  5. Finding the children of the container can be done using the method down() method. in this case we are finding the child <form> element of a donate panel. If you need to find the parents of the component use up().

Tip
Since MVC paradigm splits the code into separate layers, you can unit test them separately, e.g. test your controllers separately from the Views. Chapter 8 is dedicated to JavaSctipr testing, and it contains a section that illustrates how to arrange for testing of the model in the Ext JS version of the Save Sick Child application.

The top level window is a SSC.view.Viewport, which will contain the Header and the Donate form views.

Ext.define('SSC.view.Viewport', {
  extend: 'Ext.container.Viewport',
  requires: [
    'Ext.tab.Panel',
    'Ext.layout.container.Column'
  ],

  cls: 'app-viewport',
  layout: 'column',               // (1)
  defaults: {
    xtype: 'container'
  },

  items: [{
    columnWidth: 0.5,
    html: '&nbsp;' // Otherwise column collapses
  }, {
    width: 980,
    cls: 'main-content',
    layout: {
      type: 'vbox',              // (2)
      align: 'stretch'
    },

    items: [
      {
      xtype: 'appheader'
      },
      {
      xtype: 'container',
      minHeight: 350,
      flex: 1,

      cls: 'donate-panel',       // (3)
      layout: 'card',

      items: [{
        xtype: 'container',
        layout: 'vbox',

        items: [{
          xtype: 'component',
          html: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent ...',

          maxWidth: 550,
          padding: '80 20 0'
        }, {
          xtype: 'button',
          action: 'showform',
          text: 'DONATE NOW',
          scale: 'large',
          margin: '30 230'
        }]
      }, {
        xtype: 'donateform',
        margin: '80 0 0 0'
      }]
    }, {
      xtype: 'container',
      flex: 1
    }]
  }, {
    columnWidth: 0.5,
    html: '&nbsp;'
  }]

});
  1. Our viewport has a column layout, and will be explained after the Collapsed Code of Viewport.js Collapse Code of Viewport.js below.

  2. The vertical box layout will display the components from the items array one under another : the appheader and the container, which is explained next.

  3. The container with the class selector donate-panel includes two components, but since they are laid out as card, only one of them will be shown at a time: either the one with the "Lorem ipsum" text, or the donateform. Which one to show is mandated by the Donate controller by invoking the method setActiveItem() with the appropriate index.

The following figure shows a snapshot from WebStorm IDE, with collapsed code section just to see the big picture of what are the columns in the column layout - they are marked with arrows.

image
Figure 13. Collapsed Code of Viewport.js
Tip
Open its menu Preferences | JavaScript | Libraries and add the file ext-all-debug-w-comments.js as a global library and pressing the button F1 will display available comments about selected Ext JS element. Configuring Ext JS as external library allows you to remove Ext JS files from WebStorm project without losing context sensitive help.

In Ext JS the column layout is used when you are planning to present the information in columns as explained in the product documentation. Even though there are three columns in this layout, the entire content on this page is located in the middle column having the width of 980. The column on the left and the column on the right just hold one non-breakable space each to provide centering of the middle column in monitors with high resolution wider than 980 pixels (plus the browser’s chrome).

The width of 0.5, 980, 0.5 means to give the middle column 980 pixels and share the remaining space equally between empty columns.

Tip: There is another way to lay out this screen using Horizontal Box hbox with the pack configuration property, but we decided to keep the column layout for illustration purposes.

SASS and CSS

Take a look at the project structure shown at Collapsed Code of Viewport.js - it has sass directory, which contains several files with styles: DonateForm.scss, Header.scss, and Viewport.scss. Note that the file name extension is not css, but scss - it’s Syntactically Awesome Stylesheets (SASS). The content of the Viewport.css is shown below. In particular, if you’ve been wondering where are located the image of the boy and the background flowers - there are right there.

.app-viewport {
  background: white;
}

.main-content {
  background: url("images/bg-1.png") no-repeat;
}

.donate-panel {
  background: url("images/child-1.jpg") no-repeat right bottom, url("images/bg-2.png")
  no-repeat 90px bottom;
  border-bottom: 1px dotted #555;
}

SASS is an extension of CSS3, which allows using variables, mixins, inline imports, inherit selectors and more with CSS-compatible syntax. The simplest example of SASS syntax is to define a variable that stores some color code, e.g. $mypanel-color: #cf6cc2;. Now if you need to change the color you just change the value of the variable in one place rather than trying to find all places in a regular CSS where this color was used. But since modern Web browsers don’t understand SASS styles, they have to be converted into regular CSS before deploying your Web applications.

Ext JS includes Compass, which is an open-source CSS Authoring Framework built on top of SASS. It includes a number of modules and functions that will save your time for defining such things as border radius, gradients, transitions and more in a cross-browser fashion. For example, you write one SASS line .simple { @include border-radius(4px, 4px); }, but Compass will generate the following cross-browser CSS section:

 -webkit-border-radius: 4px 4px;
 -moz-border-radius: 4px / 4px;
 -khtml-border-radius: 4px / 4px;
 border-radius: 4px / 4px; }

See Compass documentation for more examples like the above. To manually compile your SASS into CSS you can use the command compass compile from the Command or Terminal window. This step is also performed automatically during the Sencha CMD application build. In case of the Save Sick Child application the resulting CSS file is located in build/SSC/production/resources/SSC-all.css.

We are not using any extended CSS syntax in our Save Sick Child application, but since SASS is a superset of CSS, you can use your existing CSS as is - just save it in the .scss file. If you’d like to learn more about the SASS syntax, visit the site sass-lang.com, which has tutorials and reference documentation.

In general, Ext JS substantially reduces the need for manual CSS writing by using predefined themes. Sencha offers a tutorial explaining how to use SASS and Compass for theming.

Now let’s look at the child elements of the SSC.view.Viewport. The SSC.view.Header is the simplest view. Since Save Sick Child is not a typical enterprise application with a bunch of forms and grids, we’ll use the lightest top-level container class Container where possible. The class Container gives you the most freedom in what to put inside and how to layout its child elements. Our SSC.view.Header view extends Ext.Container and contains child elements, some of which have the xtype: component, and some - container:

Ext.define("SSC.view.Header", {
  extend: 'Ext.Container',
  xtype: 'appheader',       // (1)

  cls: 'app-header',        // (2)

  height: 85,

  layout: {                 // (3)
    type: 'hbox',
    align: 'middle'
  },

  items: [{                 // (4)
    xtype: 'component',
    cls: 'app-header-logo',
    width: 75,
    height: 75
  }, {
    xtype: 'component',
    cls: 'app-header-title',
    html: 'SAVE SICK CHILD',
    flex: 1
  }, {
    xtype: 'container',      // (5)
    defaults: {
      scale: 'medium',
      margin: '0 0 0 5'
    },
    items: [{
      xtype: 'button',
      text: 'Who We Are'
    }, {
      xtype: 'button',
      text: 'What We Do'
    }, {
      xtype: 'button',
      text: 'Where We Work'
    }, {
      xtype: 'button',
      text: 'Way To Give'
    }]
  }]
});
  1. We assigned appheader as the xtype value of this view, which will be used as a reference inside the SSC.view.Viewport.

  2. cls is a class attribute of a DOM element. In this case it the same as writing class=app-header in the HTML element.

  3. The header uses hbox layout with center alignment

  4. Child components of the top container are the logo image, the text "Save Sick Child", and another container with buttons

  5. A container with button components

Let’s review the view DonateForm next, which is a subclass of Ext.form.Panel and contains the form with radio buttons, fields and labels. This component named donateform will be placed under SSC.view.Header inside SSC.view.Viewport.

Ext.define('SSC.view.DonateForm', {
  extend: 'Ext.form.Panel',
  xtype: 'donateform',
  requires: [                  // (1)
    'Ext.form.RadioGroup',
    'Ext.form.field.*',
    'Ext.form.Label'
  ],

  layout: {
    type: 'hbox',             // (2)
    align: 'stretch'
  },

  bodyStyle: {
    backgroundColor: 'transparent'
  },

  defaults: {
    margin: '0 50 0 0'
  },

  items:[{
    xtype: 'container',        // (3)
    layout: 'vbox',

    items: [{
      xtype: 'container',
      width: 200,

      items: [{
        xtype: 'radiogroup',
        fieldLabel: 'Please select or enter donation amount',
        labelAlign: 'top',
        labelSeparator: '',
        labelCls: 'donate-form-label',

        vertical: true,
        columns: 1,

        defaults: {
          name: 'amount'
        },

        items: [
          { boxLabel: '10',  inputValue: '10'  },
          { boxLabel: '20',  inputValue: '20'  },
          { boxLabel: '50',  inputValue: '50'  },
          { boxLabel: '100', inputValue: '100' },
          { boxLabel: '200', inputValue: '200' }
        ]
      }]
    }, {
      xtype: 'textfield',
      fieldLabel: 'Other amount',
      labelAlign: 'top',
      labelSeparator: '',
      labelCls: 'donate-form-label'
    }]
  }, {
    xtype: 'fieldcontainer',             // (4)
    fieldLabel: 'Donor information',
    labelAlign: 'top',
    labelSeparator: '',
    labelCls: 'donate-form-label',

    defaults: {
      allowBlank: false
    },

    items: [{
      xtype: 'textfield',
      name: 'donor',
      emptyText: 'full name'
    }, {
      xtype: 'textfield',
      emptyText: 'email'
    }, {
      xtype: 'textfield',
      emptyText: 'address'
    }, {
      xtype: 'textfield',
      emptyText: 'city'
    }, {
      xtype: 'textfield',
      emptyText: 'zip/postal code'
    }, {
      xtype: 'combobox',
      emptyText: 'state',
      store: ['Alabama', 'Alaska', ..., 'Wyoming']
    }, {
      xtype: 'combobox',
      emptyText: 'country',
      store: ['US', 'Russia']
    }]
  }, {
    xtype: 'container',        //  (5)
    layout: {
      type: 'vbox',
      align: 'center'
    },

    items: [{
      xtype: 'label',
      text: 'We accept PayPal payments',
      labelSeparator: '',
      cls: 'donate-form-label',
      width: 220
    }, {
      xtype: 'component',
      width: 220,
      html: 'Your payment will processed securely by PayPal...'
    }, {
      xtype: 'button',
      action: 'donate',
      text: 'DONATE NOW',
      scale: 'large',
      margin: '20 0 0 0'
    }, {
      xtype: 'button',
      action: 'hideform',
      margin: '10 0 0 0',
      text: 'I will donate later'
    }]
  }]

});
  1. DonateForm depends on several classes listed in the requires property. The Ext JS will check to see if these classes are present in memory, and if not, the loader will load all dependencies first, and only after the DonateForm class.

  2. Our DonateForm uses horizontal box (hbox) layout, which means that certain components or containers will be laid out next to each other horizontally. But which ones? The children of the container located in the items[] arrays hence they are the once that will be laid out horisontally in this case. But the above code contains several of items[] arrays with different level of nesting. How quickly find those that belong to the topmost container DonateForm? This is the case that clearly shows that having a good IDE can be of great help.

    Collapsed Code of Viewport.js shows a snapshot from the WebStorm IDE illustrating how can you find the matching elements in the long listings. The top level items[] arrays starts from line 23 and we see that the first element to be laid out by in hbox has the xtype: container, which in turn has some children. If you’ll move the blinking cursor of the WebStorm editor right after the firs open curly brace in line 23, you’ll see a thin blue vertical line that goes down to line 60. This is where the first object literal ends.

    Hence the second object to be governed by the hbox layout starts on line 61. You can repeat the same trick with the cursor to see where that object ends and the fieldcontainer starts . This might seem like a not overly important top, but it really saves developer’s time.

  3. The first element of the hbox is a container that internally laid out as a vbox (see DonateForm.js: an hbox with three vbox containers).Tthe radiogroup is on top and the textfield for entering Other amount at the bottom.

  4. The fieldcontainer is a light-weight Ext JS container useful to group components - the donor information in this case. It’s the central element in the hbox container shown in DonateForm.js: an hbox with three vbox containers.

  5. The right side of the hbox is another container with the vbox internal layout to show the "We accept Paypal" message, "DONATE NOW", and "I’ll donate later" buttons (see DonateForm.js: an hbox with three vbox containers). These buttons respond to clicks because

image
Figure 14. Collapsed Code of Viewport.js
Tip
Debugging of frameworks that are extensions of JavaScript in Web browsers can be difficult, because while you may be operating with, say Ext JS classes, the browser will receive regular <div>, <p> and other HTML tags and JavaScript. Illuminations is a Firebug add-on that allows to inspect elements showing not just their HTML representations, but the corresponding Ext JS classes that were used to create them.
image
Figure 15. DonateForm.js: an hbox with three vbox containers

The code review of the top portion of the Save Sick Child application is finished. Run the SSC_Top_ExtJS project and turn on the Chrome Developers Tools. Scroll to the bottom of the Network tab, and you’ll see that the browser made about 250 requests to the server and downloaded 4.5Mb in total. Not too exciting isn’t it?

On the next runs these numbers will drop to about 30 requests and 1.7Mb transferred - the browser’s caching kicked in. These numbers would be better if instead of ext-all.js we’d be linking ext.js, and even better if we’d created a custom build (see Sencha CMB section above) for the Save Sick Child application merging the application code into one file to contain only those framework classes that were actually used.

Completing Save Sick Child

In this section we’ll review the code supporting the lower half of the Save Sick Child UI, which you should import into Eclipse IDE from the file SSC_Complete_ExtJS.zip. If you see the target runtime error, read the beginning of the section "Save Sick Child: The Top Portion" for the cure. Stop the Tomcat server if running, and deploy the SSC_Complete_ExtJS under Tomcat server in the Servers view (the right-click menu, Add and Remove…​). Start Tomcat in Eclipse, right-click on the project and run it on the server. It’ll open up a Web browser pointing at http://localhost:8080/SSC_Complete_ExtJS showing the window similar to the one depicted on Save Sick Child with live charts.

image
Figure 16. Save Sick Child with live charts

This version has some additions comparing to the previous ones. Notice the bottom left panel with charts. First of all, the charts are placed inside the panel with tabs: Charts and Table. The same data can be rendered either as a chart or as a grid. Second, the charts became live thanks to ExtJS. We took snapshot of the Window shown in Save Sick Child with live charts while hovering the mouse over the pie slice representing New York, and the slice has extended from the pie showing a tooltip.

The SSC_Complete_ExtJS has more Ext JS classes comparing to SSC_Top_ExtJS. You can see more views on JavaScript classes of SSC_Complete_ExtJS. Besides, we’ve added two classes Donors.js and Campaigns.js to serve as data stores for the panels with charts and maps.

image
Figure 17. JavaScript classes of SSC_Complete_ExtJS
Login, Video, and Maps

The Login Box view is pretty small and self explanatory:

Ext.define("SSC.view.LoginBox", {
    extend: 'Ext.Container',
    xtype: 'loginbox',

    layout: 'hbox',

    items: [{
        xtype: 'container',
        flex: 1
    }, {
        xtype: 'textfield',
        emptyText: 'username',
        name: 'username',
        hidden: true
    }, {
        xtype: 'textfield',
        emptyText: 'password',
        inputType: 'password',
        name: 'password',
        hidden: true
    }, {
        xtype: 'button',
        text: 'Login',
        action: 'login'
    }]
});

The code to process the user’s logins is added to the Donate.js controller.

'button[action=login]': {
      click: this.showLoginFields
 }
...

showLoginFields: function () {
    this.getUsernameBox().show();
    this.getPasswordBox().show();
}

The bottom portion of the Windows includes several components. The video view simply reuses the HTML <video> tag we used in chapters 4 and 5. Ext JS 4.2 doesn’t offer any other solutions for embedding videos. On one hand, subclussing Ext.Component is the lightest way of including any arbitrary HTML markup. On the other hand, turning HTML into an Ext JS component allows us to use it the same way as any other Ext JS component, e.g. participate in layouts. Here’s the code of the VideoPanel.js:

Ext.define("SSC.view.VideoPanel", {
 extend: 'Ext.Component',
 xtype: 'videopanel',

 html: [
   '<video controls="controls" poster="resources/media/intro.jpg" width="390px" height="240px" preload="metadata">',
     '<source src="resources/media/intro.mp4" type="video/mp4"/>',
     '<source src="resources/media/intro.webm" type="video/webm"/>',
     '<p>Sorry, your browser doesn\'t support the video element</p>',
   '</video>'
 ]

});

The mapping part is located in the view CampaignsMap.js. Initially we tried to use the Ext.ux.GMapPanel, but it didn’t work as expected. As a workaround, we’ve added the HTML <div> element to serve as a map container.

Ext.define("SSC.view.CampaignsMap", {
 extend: 'Ext.Component',
 xtype: 'campaignsmap',

 html: ['<div class="gmap"></div>'],

 renderSelectors: {                 // (1)
     mapContainer: 'div'
 },

 listeners: {
  afterrender: function (comp) {
      var map,
          mapDiv = comp.mapContainer.dom;     // (2)

      if (navigator && navigator.onLine) {    // (3)
          try {
              map = comp.initMap(mapDiv);
              comp.addCampaignsOnTheMap(map);
          } catch (e) {
              this.displayGoogleMapError();
          }
      } else {
          this.displayGoogleMapError();
      }
  }
 },

 initMap: function (mapDiv) {                   // (4)
   // latitude = 39.8097343 longitude = -98.55561990000001
   // Lebanon, KS 66952, USA Geographic center of the contiguous United States
   // the center point of the map
   var latMapCenter = 39.8097343,
       lonMapCenter = -98.55561990000001;

   var mapOptions = {
       zoom     : 3,
       center   : new google.maps.LatLng(latMapCenter, lonMapCenter),
       mapTypeId: google.maps.MapTypeId.ROADMAP,
       mapTypeControlOptions: {
           style   : google.maps.MapTypeControlStyle.DROPDOWN_MENU,
           position: google.maps.ControlPosition.TOP_RIGHT
       }
   };

   return new google.maps.Map(mapDiv, mapOptions);
 },

 addCampaignsOnTheMap: function (map) {
  var marker,
      infowindow = new google.maps.InfoWindow(),
      geocoder   = new google.maps.Geocoder(),
      campaigns  = Ext.StoreMgr.get('Campaigns');    // (5)

  campaigns.each(function (campaign) {
      var title       = campaign.get('title'),       // (6)
          location    = campaign.get('location'),
          description = campaign.get('description');

      geocoder.geocode({
          address: location,
          country: 'USA'
      }, function(results, status) {
          if (status == google.maps.GeocoderStatus.OK) {

              // getting coordinates
              var lat = results[0].geometry.location.lat(),
                  lon = results[0].geometry.location.lng();

              // create marker
              marker = new google.maps.Marker({
                  position: new google.maps.LatLng(lat, lon),
                  map     : map,
                  title   : location
              });

              // adding click event to the marker to show info-bubble with data from json
              google.maps.event.addListener(marker, 'click', (function(marker) {
                  return function () {
                      var content = Ext.String.format(
                          '<p class="infowindow"><b>{0}</b><br/>{1}<br/><i>{2}</i></p>',
                          title, description, location);

                      infowindow.setContent(content);
                      infowindow.open(map, marker);
                  };
              })(marker));
          } else {
              console.error('Error getting location data for address: ' + location);
          }
      });
  });
 },

 displayGoogleMapError: function () {
    console.log('Error is successfully handled while rendering Google map');
    this.mapContainer.update('<p class="error">Sorry, Google Map service isn\'t available</p>');
 }
});
  1. Since we’ve added video container just by including the HTML <div> component, Ext JS wouldn’t create an ID for it. We didn’t want to create an ID manually hence used the property renderSelectors allows to map an arbitrary name to a DOM selector. In this case such key-value pair is mapContainer: 'div'. The mapContainer is a cross-browser wrapper for DOM elements (see product documentation).

  2. Querying the DOM to find the mapContainer defined in the renderSelectors property. Note that we are getting the reference to this DOM element after the view is rendered in the event handler function afterrender. The object comp will be provided to this handler, and it points at the instance of the current component, which is SSC.view.CampaignsMap.

  3. If Google Map is not available, display an error message as in If Google Maps server is not responding. This code was added after one of the authors was testing this code while sitting in the plane with no Internet connection. But checking the status of navigator.onLine may not be a reliable indicator of the offline status, so we’ve wrapped it into a try/catch block just to be sure.

  4. The rest of the code in this class has the same mapping functionality as described in Chapter 3 in section "Adding Geolocation Support".

  5. The data for the campaign information are coming from the store Campaigns.js located in the folder store. The store manager can find the reference to the store either if by assigned storeId or by name Campaigns listed in the stores array in the app.js:

  6. We are configuring the mapping panel to get the information about the campaign title, location, and description from the fields with corresponding names from the store SSC.store.Campaigns that’s shown right after app.js below.

image
Figure 18. If Google Maps server is not responding
Ext.application({
    name: 'SSC',

    views: [
        'CampaignsMap',
        'DonateForm',
        'DonorsPanel',
        'Header',
        'LoginBox',
        'VideoPanel',
        'Viewport'
    ],

    stores: [
        'Campaigns',
        'Donors'
    ],

    controllers: [
        'Donate'
    ],

    autoCreateViewport: true
});

In Chapter 5 the information about campaigns was taken from a file with JSON formatted data. In this version the data will be taken from the class SSC.store.Campaigns that’s shown next. This class extends Ext.data.JsonStore, which is a helper class for creating stores based on the JSON data. The class JsonStore is a subclass of more generic Ext.data.Store, which implements client side caching of Model objects, can load the data via the Proxy object, and supports sorting and filtering.

Ext.define('SSC.store.Campaigns', {
    extend: 'Ext.data.JsonStore',

    fields: [                               // (1)
        { name: 'title',       type: 'string' },
        { name: 'description', type: 'string' },
        { name: 'location',    type: 'string' }
    ],

    data: [{                           // (2)
        title:       'Lorem ipsum',
        description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
        location:    'Chicago, IL'
    }, {
        title:       'Donors meeting',
        description: 'Morbi mollis ante at ante posuere tempor.',
        location:    'New York, NY'
    }, {
        title:       'Sed tincidunt magna',
        description: 'Donec ac ligula sit amet libero vehicula laoreet',
        location:    'Dallas, TX'
    }, {
        title:       'Fusce tellus dui',
        description: 'Sed accumsan nibh sapien, interdum ullamcorper velit.',
        location:    'Miami, FL'
    }, {
        title:       'Aenean lorem quam',
        description: 'Pellentesque habitant morbi tristique senectus',
        location:    'Fargo, ND'
    }]
});
  1. We have not created a separate model class for each campaign, because this information is used only in one place. The fields array defines our inline model, which consist of objects (data) containing the properties title, description, and location.

  2. Hard-coded data for the model

Chart and Table Panels

The bottom left area of the Save Sick Child window is occupied by a subclass of Ext.tab.Panel. The name of our view is SSC.view.DonorsPanel, and it contains two tabs: Chart and Table. Accordingly, the class definition start with declaring dependencies for the Ext JS classes that supports charts and a data grid.

Charting is an important part of many enterprise applications, and Ext JS framework offers solid chart drawing capabilities without the need to install any plugins. We’d like to stress that both Chart and Table panels use the same data - they just provide different vies of the data. Let’s review the code now.

Ext.define("SSC.view.DonorsPanel", {
 extend: 'Ext.tab.Panel',
 xtype: 'donorspanel',
 requires: [
     'Ext.chart.Chart',
     'Ext.chart.series.Pie',
     'Ext.grid.Panel',
     'Ext.grid.column.Number',
     'Ext.grid.plugin.CellEditing'
 ],

 maxHeight: 240,
 plain: true,                  // (1)

 items: [{
   title: 'Chart',             // (2)
   xtype: 'chart',
   store: 'Donors',
   animate: true,
   legend: {
       position: 'right'
   },
   theme: 'Base:gradients',
   series: [{
       type: 'pie',            // (3)
       angleField: 'donors',
       showInLegend: true,
       tips: {                                // (4)
           trackMouse: true,
           renderer: function (storeItem ) {

             var store = storeItem.store,
                 total = 0;

             store.each(function(rec) {
                 total += rec.get('donors');        // (5)
             });

             this.update(Ext.String.format('{0}: {1}%',
                 storeItem.get('location'),               // (6)
                 Math.round(storeItem.get('donors') / total * 100)));
           }
       },
       highlight: {
           segment: {
               margin: 20
           }
       },
       label: {                  // (7)
           field: 'location',
           display: 'horizontal',
           contrast: true,
           renderer: function (label, item, storeItem) {
               return storeItem.get('donors');
           }
       }
   }]
 }, {
     title: 'Table',            // (8)
     xtype: 'gridpanel',
     store: 'Donors',
     columns: [                 //  (9)
         { text: 'State',  dataIndex: 'location', flex: 1},
         { text: 'Donors', dataIndex: 'donors',
                  xtype: 'numbercolumn', format: '0', editor: 'numberfield' }
     ],
     plugins: [{
         ptype: 'cellediting'
     }]
 }]

});
  1. By default, the top portion of the tab panel was showing a blue background, which we didn’t like and turned this style off to give a little cleaner look to the tabs.

  2. The first panel is an instance of xtype chart, which gets the data from the store object Donors.

  3. Configuring and creating a Pie Chart. The width of each sector is controlled by the angleField property, which is mapped to the field donors defined in the store SSC.store.Donors (see the code listing below).

  4. We’ve overriden the config renderer to provide custom styling for each element. In particular, we’ve configured tips to be displayed on mouse hover.

  5. Calculating total for proper display of the percentages on mouse hover.

  6. The label for each pie sector is retrieved from the field location defined in the store SSC.store.Donors shown in the code listing below.

  7. Displaying the chart legend on the right side. If the user moves the mouse over the legend, the pie sectors start to animate.

  8. The second tab contains and instance of xtype gridpanel. Note that the store object is the same as the Chart panel uses.

  9. The grid has two columns. One of them is a simple text, but the other is rendered as a numbercolumn that displays the data according to a format string.

The store Donors contains the hard-coded data for our pie chart as well as for the table. In the real world, the data would be retrieved from the server side. Since we were getting ready to consume JSON data (not implemented), our Donors class.

Ext.define('SSC.store.Donors', {
    extend: 'Ext.data.JsonStore',

    fields: [
        { name: 'donors',   type: 'int' },      // (1)
        { name: 'location', type: 'string' }
    ],

    data: [                                     // (2)
        { donors: 48, location: 'Chicago, IL' },
        { donors: 60, location: 'New York, NY' },
        { donors: 90, location: 'Dallas, TX' },
        { donors: 22, location: 'Miami, FL' },
        { donors: 14, location: 'Fargo, ND' },
        { donors: 44, location: 'Long Beach, NY' },
        { donors: 24, location: 'Lynbrook, NY' }
    ]
});
  1. Defining inline model

  2. Hard-coded data for the model

The data located in the store SSC.store.Donors can be rendered not only as a chart, but in a tabular form as well. To switch to a table view shown in The Table tab the user has to click on the tab Table.

image
Figure 19. The Table tab

The following code fragment from DonorsPanel is all it takes to render the donors' data as a grid. The xtype of this component is gridpanel. For illustration purposes we made the column Donors editable - double click on the a cell with the number and it’ll turn this field into a numeric field as shown in The Table tab for the location Fargo, ND.

{
 title: 'Table',
 xtype: 'gridpanel',
 store: 'Donors',      // (1)
 columns: [
     { text: 'City/State',  dataIndex: 'location', flex: 1},
     { text: 'Donors', dataIndex: 'donors', xtype: 'numbercolumn', format: '0', editor: 'numberfield' }
 ],
 plugins: [{
     ptype: 'cellediting'      // (2)
 }
  1. Reusing the same store as in chart panel

  2. We are using one of the exiting Ext JS plugins here, namely Ext.grid.plugin.CellEditing to allow editing the cells of the Donors column. In this example we are using an existing Ext JS editor numberfield in the Donors column. Since we don’t work with decimal numbers here, the editor uses format:0. To make the entire row of the grid editable use the plugin Ext.grid.plugin.RowEditing. If you wanted to create some custom plugin for a cell, you’d need to define it by the rules for writing Ext JS plugins.

Tip
Modify the any value in the Donor’s cell and switch to the Chart panel. You’ll see that the size corresponding pie sector has changed accordingly.

The total number of lines of code in DonorsPanel and in the store Donors is under 100. Being able to create a tab panel with chart and grid with almost no manual coding is quite impressive, isn’t it?

To complete Save Sick Chile code review, we need to mention the icons located in the bottom of the ViewPort.js shown on The Viewport footer.

image
Figure 20. The Viewport footer

This footer was implemented in the code snippet below. We’ve implemented these little icons as regular images.

 items: [{
     xtype: 'component',
     flex: 1,
     html: '<strong>Project SSC_Complete_ExtJS:</strong>'
 }, {
     src: 'resources/images/facebook.png'
 }, {
     src: 'resources/images/google_plus.png'
 }, {
     src: 'resources/images/twitter.png'
 }, {
     src: 'resources/images/rss.png'
 }, {
     src: 'resources/images/email.png'
 }]
Tip
There is a more efficient way to do this by using a numeric character code that renders as image (see the glyph config property). The Pictos library offers hundreds of such tiny images in both vector or PNG form.

Ext JS library contains lots of JavaScript code, but it allows developers produce nice looking applications with a fraction of a code comparing to other frameworks. Also, despite the fact that this version of Save Sick Child offers more functionality than those from the previous chapters, we’ve had to write the bare minimum of the CSS code thanks to Ext JS theming.

Building Production Version of Save Sick Child

Run the completed version of our application in Chrome browser having Developers Tools turned on. Go to the Network tab and scroll to the bottom. You’ll see the message reporting that the browser made 365 requests to the server and downloaded 6.4Mb of content as in The size of development version of Save Sick Child.

image
Figure 21. The size of development version of Save Sick Child

Now let’s create production version with all JavaScript merged into one file. Open the Terminal or Command window and change directory into the Eclipse workspace directory where your project was created (e.g. …​/SSC_Complete_ExtJS/WebContent) and enter the command described in the Sencha CMD section earlier in this chapter:

sencha app build

The production version of the Save Sick Child application will be generated in the directory …​/SSC_Complete_ExtJS/WebContent/build/SSC/production. All your application JavaScript code will be merged with the required classes of Ext JS framework into one file all-classes.js, which in our case weighs 1.2Mb. The generated CSS file SSC-all.css will be located in the directory resources. All images are there too. This is how the production version of index.html will look like:

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>SSC</title>
    <script src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
<link rel="stylesheet" href="resources/SSC-all.css"/>
<script type="text/javascript" src="all-classes.js"></script>
</head>
<body></body>
</html>

Deploy the content of the production under any Web server and load this version of the application in the Chrome with Developer Tools turned on. This time the number of dowloaded bytes is three times lower (2.3Mb). Ask your Web Server administrator to enable gzip or deflate, and the size of the JavaScript will go down from 1.2Mb to 365KB. The size of other resources will decrease even more. Don’t forget that we are loading a 500Kb video file intro.mp4. The number of server requests went down to 55, but more that 30 of them were Google Map API calls.

image
Figure 22. The size of production version of Save Sick Child

Productive Enterprise Web Development with Ext JS and CDB

Authors of this book work for the company called Farata Systems, which has developed an open source freely available software Clear Toolkit for Ext JS, and the code generator and Eclipse IDE plugin CDB comes with it. CDB is a productivity tool that was created specifically for the applications that need to retrieve, manipulate, and save the data in some persistent storage.

Such applications are known as CRUD applications because they perform Create-Retrieve-Update-Delete operations with data. If the server side of your Web application is developed in Java, with CDB you can easily generate a CRUD application, where Ext JS front end communicates the Java back end. In this section you will learn how jump start development of such CRUD Web applications.

Important
Familiarity with core Java concepts like classes, constructors, getters and setters, and annotations is required for understanding of the materials of this section.

The phrase to be more productive means to write less code while producing the results faster. This is what CDB is for, and you’ll see it helps you to integrate the client side with the back end using the RPC style and how to implements data pagination for your application.

Part One: Ext JS MVC Application Scaffolding

In this part we’ll cover the following topics:

  • What is Clear Toolkit for Ext JS

  • How to create an Ext JS MVC front end for a Java-based project

  • How to deploy and run your first Ext JS and Java application on Apache Tomcat server

Clear Toolkit for Ext JS includes the following:

  • Clear Data Builder - an Eclipse plugin that supports code generation Ext JS MVC artifacts based on the code written in Java. CDB comes with wizards to start new project with plain Java or with popular frameworks like Hibernate, Spring, MyBatis.

  • Clear JS - a set of JavaScript components that extends Ext JS standard components. In particular, it includes a ChangeObject that traces the modifications of any item in a store.

  • Clear Runtime - Java components that implements server side part of ChangeObject, DirectOptions an others.

CDB distribution available as plug-in for a popular among Java developers Eclipse IDE. The current update site of CDB is located here. The current version is 4.1.4. You can install this plug-in via the Install New Software menu in Eclipse IDE. The Verifying CDB installation shows "Clear Data Builder for Ext JS feature" in the list of Installed Software in your Eclipse IDE, which means that CDB is installed.

Important
You have to have work with "Eclipse IDE for Java EE Developers", which includes plugins for automation of the Web application development.
image
Figure 23. Verifying CDB installation

Clear Data Builder comes with a set of prepared examples that demonstrate the integration with popular Java frameworks - MyBatis, Hibernate, and Spring. There is also a plain Java project example that doesn’t use any persistence frameworks. Let’s start with the creation of the new project by selecting Eclipse menu File → New → Other → Clear. You’ll see a window similar to New CDB Project Wizard.

image
Figure 24. New CDB Project Wizard

Name the new project episode_1_intro. CDB supports different ways of linking the Ext JS framework to the application. CDB automatically copies the Ext JS framework under the Web server (Apache Tomcat in our case). We’re going to use this local Ext JS URL, but you can specify any folder in your machine and CDB will copy the Ext JS file from there into your project. You can also use Ext JS from the Sencha’s CDN, if you don’t want to store these libraries inside your project. Besides, using a common CDN will allow Web browser to reuse the cached version of Ext JS.

For this project we are not going to use any server-side persistence frameworks like MyBatis or Hibernate. Just click the button Finish, and you’ll see some some initial CDB messages on the Eclipse console. When CDB runs for the first time it creates in your project’s WebContent folder the directory structure recommended by Sencha for MVC applications. It also generates index.html for this application, which contains the link to the entry point of our Ext JS application.

CDB generates an empty project with one sample controller and one view - Viewport.js. To run this application, you need to add the newly generated Dynamic Web Project to Tomcat and start the server (right-click on the Tomcat in the Servers view of Eclipse IDE).

image
Figure 25. Adding web project to Tomcat

Open this application in your Web browser at http://localhost:8080/episode_1_intro . Voila! In less than a couple of minutes we’ve created a new Dynamic Web Project with the Ext JS framework and one fancy button as shown on Running scaffolded application.

image
Figure 26. Running scaffolded application

The next step is to make something useful out of this basic application.

Part Two: Generating a CRUD application

The Part Two of the CDB section covers the process of creation of a simple CRUD application that uses Ext JS and Java. We’ll go through the following steps:

  • Create a plain old Java object (POJO) and the corresponding Ext.data.Model

  • Create a Java service and populate Ext.data.Store with data from service

  • Use the auto-generated Ext JS application

  • Extend the auto-generated CRUD methods

  • Use ChangeObject to track the data changes

Now let’s use CDB to create a CRUD application. You’ll learn how turn a POJO into an Ext JS model, namely:

  • how to populate the Ext JS store from a remote service

  • how to use automatically generated UI for that application

  • how to extend the UI

  • what the ChangeObject class is for

First, we’ll extend the application from Part One - the CRUD application needs a Java POJO. To start, create a Java class Person in the package dto. Then add to this class the properties (as well as getters and setters) firstName, lastName, address, ssn and phone and id. Add the class constructor that initializes these properties as shown in the code listing below.

Person data transfer object
package dto;

import com.farata.dto2extjs.annotations.JSClass;
import com.farata.dto2extjs.annotations.JSGeneratedId;

@JSClass
public class Person {

  @JSGeneratedId
  private Integer id;
  private String firstName;
  private String lastName;
  private String phone;
  private String ssn;

  public Person(Integer id, String firstName, String lastName,
                                    String phone, String ssn) {
  	super();
  	this.id = id;
  	this.firstName = firstName;
  	this.lastName = lastName;
  	this.phone = phone;
  	this.ssn = ssn;
  }

  // Getters and Setters are omitted for brevity
}

You may also add a toString() method to the class. Now you’ll need the same corresponding Ext JS model for the Java class Person. Just annotate this class with the annotation @JSClass to have CDB generate the Ext JS model.

The next step is to annotate the id field with the CDB annotation @JSGeneratedId. This annotation instructs CDB to threat this field as an auto generated id. Let’s examine the directory of Ext JS MVC application to see what’s inside the model folder. In the JavaScript section there is the folder dto which corresponds to the Java dto package where the PersonModel resides as illustrated on Generated from Java class Ext JS model.

image
Figure 27. Generated from Java class Ext JS model

Clear Data Builder generated two files as recommended by the Generation Gap pattern, which is about keeping the generated and handwritten parts separate by putting them in different classes linked by inheritance. Let’s open the person model. In our case the PersonModel.js is extended from the generated _PersonModel.js. Should we need to customize this class, we’ll do it inside the Person.js, but this underscore-prefixed file will be regenerated each and every time when we change something in our model. CDB follows this pattern for all generated artifacts - Java services, Ext JS models and stores. This model contains all the fields from our Person DTO.

Now we need to create a Java service to populate the Ext JS store with the data. Let’s create a Java interface PersonService in the package service. This service will to return the list of Person objects. This interface contains one method -List<Person> getPersons().

To have CDB to expose this service as a remote object, we’ll use the annotation called @JSService. Another annotation @JSGenetareStore will instruct CDB to generate the store. In this case CDB will create the destination-aware store. This means that store will know from where to populate its content. All configurations of the store’s proxies will be handled by the code generator. With @JSFillMethod annotation we will identify our main read method (the "R" from CRUD).

Also it would be nice to have some sort of a sample UI to test the service - the annotation @JSGenerateSample will help here. CDB will examine the interface PersonService, and based on these annotations will generate all Ext JS MVC artifacts (models, views, controller) and the sample application.

PersonService interface annotated with CDB annotations
@JSService
public interface PersonService {
    @JSGenerateStore
    @JSFillMethod
    @JSGenerateSample
    List<Person> getPersons();
}

When the code generation is complete, you’ll get the implementation for the service - PersonServiceImpl. The store folder inside the application folder (WebContent\app) has the Ext JS store, which is bound to the previously generated PersonModel. In this case, CDB generated store that binds to the remote service.

image
Figure 28. Structure of store and model folders

All this intermediate translation from the JavaScript to Java and from Java to JavaScript is done by DirectJNgine, which is a server side implementation of the Ext Direct Protocol. You can read about this protocol in Ext JS documentation.

CDB has generated a sample UI for us too. Check out the samples directory shown on Folder with generated UI files.

image
Figure 29. Folder with generated UI files

CDB has generated SampleController.js, SampleGridPanel.js, and the Ext JS application entry point sampleApp.js. To test this application just copy the file SampleController.js into the controller folder, SampleGridPanel.js panel into the view folder, and the sample application in the root of the WebContent folder. Change the application entry point with to be sampleApp.js in the index.html of the Eclipse project as shown below.

<script type="text/javascript" src="sampleApp.js"></script>

This is how the generated UI of the sample application looks like Scaffolded CRUD application template.

image
Figure 30. Scaffolded CRUD application template

On the server side, CDB also follows the Generation Gap Pattern and it generated stubs for the service methods. Override these methods when you’re ready to implement the CRUD functionality, similar to the below code sample.

Implementation of PersonService interface
package service;
import java.util.ArrayList;
import java.util.List;

import clear.data.ChangeObject;
import dto.Person;
import service.generated.*;

public class PersonServiceImpl extends _PersonServiceImpl { // (1)

  @Override
  public List<Person> getPersons() {                        // (2)
      List<Person> result = new ArrayList<>();
      Integer id= 0;
      result.add(new Person(++id, "Joe", "Doe",
                      "555-55-55", "1111-11-1111"));
      result.add(new Person(++id, "Joe", "Doe",
                      "555-55-55", "1111-11-1111"));
      result.add(new Person(++id, "Joe", "Doe",
                      "555-55-55", "1111-11-1111"));
      result.add(new Person(++id, "Joe", "Doe",
                      "555-55-55", "1111-11-1111"));
      return result;                    // (3)
  }

  @Override
  public void getPersons_doCreate(ChangeObject changeObject) { // (4)
      Person dto = (Person) deserializeObject(
                      (Map<String, String>) changeObject.getNewVersion(),
                      Person.class);

      System.out.println(dto.toString());
  }

  @Override
  public void getPersons_doUpdate(ChangeObject changeObject) { // (5)
      // TODO Auto-generated method stub
      super.getPersons_doUpdate(changeObject);
  }

  @Override
  public void getPersons_doDelete(ChangeObject changeObject) { // (6)
      // TODO Auto-generated method stub
      super.getPersons_doDelete(changeObject);
  }
}
  1. Extend the generated class and provide the actual implementation

  2. The getPerson() is our retrieve method (the R in CRUD)

  3. For this sample application we can use java.util.ArrayList class as in-memory server side storage of the Person objects. In the real world applications you’d use a database or other persistent storage

  4. fillmethod+doCreate() is our create method (the C in CRUD)

  5. fillmethod+` doUpdate` is our update method (the U in CRUD)

  6. fillmethod+` doDelete` is our delete method (the D in CRUD)

Click on the Load menu on the UI, and the application will retrieve four persons from our server

To test the rest of the CRUD methods, we’ll ask the user to insert one new row, modify three existing ones and remove two rows using the generated Web client. The Clear.data.DirectStore object will automatically create a collection of six `ChangeObject`s - one to represent a new row, three to represent the modified ones, and two for the removed rows.

When the user clicks on the Sync UI menu the changes will be sent to the corresponding do…​ remote method. When you sync() a standard Ext.data.DirectStore Ext JS is POST-ing new, modified and deleted items to the server. When the request is complete the server’s response data is applied to the store expecting that some items can be modified by the server. In case of Clear.data.DirectStore instead of passing around items, we pass the deltas, wrapped in the ChangeObject.

Each instance of the ChangeOject contains the following:

  • newVersion - it’s an instance of the newly inserted or modified item. On the Java side it’s available via getNewVersion().

  • prevVersion - it’s an instance of the deleted of old version of modified item. On the Java side it’s available via getPrevVersion().

  • array of changepropertyNames if this ChangeObject represents an update operation.

The rest of ChangeObject details described on the Clear Toolkit Wiki.

The corresponding Java implementation of ChangeObject is available on the server side and Clear Toolkit passes ChangeObject instances to the appropriate do* method of the service class. Take a look at the getPersons_doCreate() method from Implementation of PersonService interface. When the server needs to read the new or updated data arrived from the client your Java class has to invoke the method changeObject.getNewVersion(). This method will return the JSON object that you need to deserialize into the object Person. This is done in Implementation of PersonService interface and looks like this.

 Person dto = (Person) deserializeObject(
            (Map<String, String>) changeObject.getNewVersion(),Person.class);

When the new version of the Person object is extracted from the ChangeObject you can do with it whatever has to be done to persist it in the appropriate storage. In our example we just print the new person information on the server-side Java console. This is why we said earlier, that it may be a good idea to provide a pretty printing feature on the class Person by overriding method toString(). Similarly, when you need to do a delete, the changeObject.getPrevVersion() would give you a person to be deleted.

Part Three: Data Pagination

The pagination feature is needed in almost every enterprise web application. Often you don’t want to bring all the data to the client at once - a page by page feed brings the data to the user a lot faster. The user can navigate back and forth between the pages using pagination UI components. To do that, we need to split our data on the server side into chunks, to send them page by page by the client request. Implementing pagination is the agenda for this section. We’ll do the following:

  • Add the data pagination to our sample CRUD application:

    • Add the Ext.toolbar.Paging component

    • Bind both grid and pagingtoolbar to the same store

    • Use DirectOptions class to read the pagination parameters

We are going to improve our CRUD application by adding the paging toolbar component bound to the same store as the grid. The class DirectOptions will handle the pagination parameters on the server side.

So far CDB has generate the UI from the Java back end service as well as the Ext JS store and model. We’ll refactor the service code from previous example to generate more data (a thousand objects) so we have something to paginate, see below.

Refactored implementation of PersonService Interface
public class PersonServiceImpl extends _PersonServiceImpl {
  @Override
    public List<Person> getPersons() {
        List<Person> result = new ArrayList<>();
        for (int i=0; i<1000; i++){
            result.add(new Person(i, "Joe", "Doe", "555-55-55",
                                                   "1111-11-1111"));
        }
        return result;
    }
}

If you’ll re-run the application now, the Google Chrome Console will show that PersonStore is populated with one thousand records. Now we’ll add the the Ext JS paging toolbarpaging UI component to the file sampleApp.js as shown below.

Sample Application Entry
Ext.Loader.setConfig({
	disableCaching : false,
	enabled : true,
	paths : {
		episode_3_pagination : 'app',
		Clear : 'clear'
	}
});

Ext.syncRequire('episode_3_pagination.init.InitDirect');
// Define GridPanel
var myStore = Ext.create('episode_3_pagination.store.dto.PersonStore',{}); //(1)
Ext.define('episode_3_pagination.view.SampleGridPanel', {
	extend : 'Ext.grid.Panel',
	store : myStore,
	alias : 'widget.samplegridpanel',
	autoscroll : true,
	plugins : [{
		ptype : 'cellediting'
	}],
	dockedItems: [
		{
			xtype: 'pagingtoolbar',		//(2)
			displayInfo: true,
			dock: 'top',
			store: myStore			//(3)
		}
	],
	columns : [
		{header : 'firstName', dataIndex : 'firstName',
		              editor : {xtype : 'textfield'}, flex : 1 },
		{header : 'id', dataIndex : 'id', flex : 1 },
		{header : 'lastName', dataIndex : 'lastName',
		              editor : {xtype : 'textfield'}, flex : 1 },
		{header : 'phone', dataIndex : 'phone',
		              editor : {xtype : 'textfield'}, flex : 1 },
		{header : 'ssn', dataIndex : 'ssn',
		              editor : {xtype : 'textfield'}, flex : 1 }],
	tbar : [
		{text : 'Load', action : 'load'},
		{text : 'Add', action : 'add'},
		{text : 'Remove', action : 'remove'},
		{text : 'Sync', action : 'sync'}
		]
	});
// Launch the application
Ext.application({
	name : 'episode_3_pagination',
	requires : ['Clear.override.ExtJSOverrider'],
	controllers : ['SampleController'],
	launch : function() {
		Ext.create('Ext.container.Viewport', {
			items : [{
				xtype : 'samplegridpanel'
			}]
		});
	}
});
  1. Manual store instantiation - create a separate variable myStore for this store with empty config object

  2. Adding the xtype pagingtoolbar to this component docked items property to display the information and dock this element at the top.

  3. Now the paging toolbar is also connected to same store.

The next step is to fix the automatically generated controller to take care of the loading of data on click of Load button as shown in the code below.

Controller for sample application
Ext.define('episode_3_pagination.controller.SampleController', {
	extend: 'Ext.app.Controller',
	stores: ['episode_3_pagination.store.dto.PersonStore'],
	refs: [{								//(1)
		ref: 'ThePanel',
		selector: 'samplegridpanel'
	}],

	init: function() {
		this.control({
			'samplegridpanel button[action=load]': {
				click: this.onLoad
			}
		});
	},

	onLoad: function() {
		// returns instance of PersonStore
		var store = this.getThePanel().getStore();		//(2)
		store.load();
	}
});
  1. Bind the store instance to our grid panel. In controller’s refs property we’re referencing our simplegrid panel with ThePanel alias.

  2. In this case there is no need to explicitly retrieve the store instance by name. Instead, we can use getters getPanel() and getStore() automatically generated by the Ext JS framework.

When the user clicks the button next or previous the method loadPage of the underlying store is called. Let’s examine the directprovider URL - the server side router of the remoting calls - to see how the direct request looks like. Open Google Chrome Developer Tools from the menu View → Developer, refresh the Web page and go to the Network tab. You’ll see that each time the user clicks on the next or previous buttons on the pagination toolbar the component sends directOptions as a part of the request.

image
Figure 31. Request payload details

The default Ext Direct request doesn’t carry any information about the page size. Clear JS has the client side extension of the Ext JS framework that adds some extra functionality to Ext.data.DirectStore component to pass the page start and limit values to the server side. At this point, the directOptions request property (see Request payload details) can be extracted on the server side to get the information about the page boundaries. Let’s add some code to the PersonServiceImpl.java. At this point the pagination doesn’t work. The server sends the entire thousand records, because it doesn’t know that the data has to be paginated. We’ll fix it in the following listing.

Implementation of PersonService With Pagination
package service;
import java.util.ArrayList;
import java.util.List;

import clear.djn.DirectOptions;			//(1)

import dto.Person;
import service.generated.*;

public class PersonServiceImpl extends _PersonServiceImpl {
	@Override
	public List<Person> getPersons() {
		List<Person> result = new ArrayList<>();
		for (int i=0; i<1000; i++){
			result.add(new Person(i, "Joe", "Doe", "555-55-55",
			                                        "1111-11-1111"));
		}
		//(2)
		int start = ((Double)DirectOptions.getOption("start")).intValue();
		int limit = ((Double)DirectOptions.getOption("limit")).intValue();

		limit = Math.min(start+limit, result.size() );		//(3)
		DirectOptions.setOption("total", result.size());	//(4)
		result = result.subList(start, limit);			//(5)

		return result;
	}
}
  1. On the server side there is a special object called DirectOptions, which comes with Clear Toolkit.

  2. We want to monitor the start and in limit values (see Request payload details).

  3. Calculate the actual limit. Assign the size of the data collection to the limit variable if it’s less than the page size (start+limit).

  4. Notify the component about the total number of elements on the server side by using DirectOptions.setOption() method with total option.

  5. Before returning the result, create a subset, an actual page of data using the method java.util.List.sublist() which produces the view of the portion of this list between indexes specified by the start and the limit parameters.

As you can see from the Network tab in Scaffolded CRUD application template, we’ve limited the data load to 25 elements per page. Clicking on next or previous buttons will get you only a page worth of data. The Google Chrome Developers Tools Network tab shows that that we are sending the start and limit values with every request, and the response contains the object with 25 elements.

If you’d like to repeat all of the above steps on you own, watch the screencasts where we demonstrate all the actions described in the section on CDB.

Summary

Creation of enterprise web applications involves many steps that need to be done by developers. But with the right set of tools the repetitive steps can be automated. Besides, Ext JS class rich component library and themes allows you to lower the amount of manual programming.

Remember the DRY principle - don’t repeat yourself. Try to do more with less efforts. This rather long chapter will help your to get started with Ext JS and, if you need to develop CRUD applications, CDB will make you more productive.