Skip to content

Latest commit

 

History

History
1819 lines (1337 loc) · 106 KB

ch6_ext_js.asciidoc

File metadata and controls

1819 lines (1337 loc) · 106 KB

Enterprise Considerations

The content of this part justifies having the word Enterprise on this book’s cover.

In Chapter 4 you’ll learn how to use rich and feature complete frameworks - Ext JS from Sencha. While using this framework may be an overkill for a small Web site, it’s pretty popular in the enterprise world, where rich-looking UI is required. Besides learning how to work with this framework, you’ll build a new version of the Save The Child application in Ext JS. In this version we’ll introduce an interactive chart (a popular feature for enterprise dashboards) and a data grid (any enterprise app uses grids).

Chapter 5 is a review of productivity tools used by enterprise developers (NPM, Grunt, Bower,Yeoman, CDB). It’s about build tools, code generators, and managing dependencies (a typical enterprise application uses carious software that need to work in harmony).

Chapter 6 is dedicated to dealing with issues that any mid-to-large enterprise Web application is facing: how to modularize the application to reduce the load time and make it more responsive. Our sample application Save The Child will be divided into modules with the help of RequireJS framework.

Chapter 7 Test-Driven Development (TDD) is a way of writing less buggy applications. TDD was originated in large projects written in such languages as Java, C++, or C#, and now it’s adopted by HTML5 community. After reviewing how to do TDD in JavaScript, we’ll show how to introduce testing into the Save The Child application.

Chapter 8 is about WebSockets - a new HTML5 API that can be a game changer for enterprise Web applications that need to have as fast communication with the servers as possible (think financial trading applications or online auctions). We’ll show how to add an auction to our sample charity application.

Chapter 9 is a brief overview of various Web application security issues. While small Web sites often forget dealing with security vulnerabilities, this subject can’t be ignored in the enterprise World.

Developing Web Applications in 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.

JavaScript Frameworks

The word framework implies that there is some pre-created "software frame", and application developers need to fit their business-specific code inside such a frame. Why someone would want to do this as opposed to having a full freedom in developing your application code the way you want? The reason being that most enterprise projects are developed by teams of software engineers, and having an agreed-upon structure of the application with clear separation of software layers can make the entire process of development more productive.

There are JavaScript frameworks that are mainly forcing developers to organize application code in layers implementing the Model-View-Controller design pattern. There are more than a dozen MVC JavaScript frameworks that are being used by professional developers: Backbone.js, ExtJS, AngularJS, Ember.js, Knockout just to name a few.

Note
Ext JS also supports MVC, and you can read about it later in this chapter in section titled MVC in Ext JS.
Tip
There is an excellent Web site called TodoMVC, which shows the examples of implementing the same application (a Todo list) using various popular frameworks. Studying the source code of this application implemented in several frameworks can help you in selecting the one for your project.
Note
To keep the size of this book manageable, we were not able to review more of JavaScript frameworks. But if you’d ask us to name one more great JavaScript framework that didn’t make it into this book, we would recommend you to learn AngularJS from Google. There are lots of free online resources on AngularJS that Jeff Cunningham has collected all in one place GitHub.

Besides splitting the code into tiers, frameworks may offer a number of pre-fabricated UI components and build tools, and Ext JS is one of such frameworks.

Note
If you decide to develop your application with Ext JS, you don’t need to use jQuery library.

What This Chapter Covers

The title clearly states that this chapter is about Ext JS framework. Providing detailed coverage of Ext JS in on chapter is almost mission impossible because of the vast variety of features this framework offers. Consider this chapter a hands-on overview of Ext JS. 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 The 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.

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. We just mentioned the frameworks that help better organizing or modularizing your project, 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. Your single HTML file (index.html) will just include three files in the head section: one with Ext JS framework, one CSS file, and one app.js, but the <body> section will be empty.

  2. Ext JS includes a comprehensive library of JavaScript-based classes that can help you with 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 processing 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 chapter 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.

Note
Typically, you shouldn’t be using the all files. We recommend you to use the file ext.js and Sencha CMD tool to create a customized version of Ext JS library to be included with your application. You can find more details in the section Sencha CMD later in this chapter.

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.

  • 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 12.

Note
Ext JS framework is large so be prepared that your application will weigh at least 1Mb. This is not an issues for enterprise applications that run on fast networks. But if you need to create a small consumer-oriented 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. On the other hand, 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 publishes 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

Before we’ll explain how things work in Ext JS, we’ll develop a Hello World application. But the section where we’ll review the code of the Save The 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.

Configuration Options

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 defined at the prototype level and changing a value 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 see 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 using a 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. For example, the documentation for Ext.panel.Panel lists 116 available configs that you can set on the panel instance.

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 The 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 can go here
});

In the next section we’ll automate the process of creating of Hello World application.

Generating Applications With 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, you’ll see the folder hello of the structure shown on the figure 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). Here’s how the generated index.html file looks:

<!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 The UI of our Sencha CMD-generated Application. 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. The UI of our Sencha CMD-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 The UI of our Sencha CMD-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. Your application can also integrate with native menus, file dialogs and access the file system.

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 The Child application.

Tip
Sencha CMD 3.1 comes with embedded Jetty 8.1.7 Web server. Open the Terminal or command window and run the following command to serve your Web application on port 8080 (can be another port): sencha fs web -port8080 start -map /path/to/app/docrootdir.

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.

Which Ext JS Distribution to Use?

First you need to select the packaging of the Ext JS framework that fits your needs. You may select its minimized version to be used in production or a larger and commented version with detailed comments and error messages. As we mentioned earlier in this chapter, 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 files with the minimized production version of Ext JS are called ext-all.js and ext.all. We usually pick the latter one. Its size is more than 1Mb, 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.js"></script>

Declaring, Loading and Instantiating Classes

Pure JavaScript doesn’t have classes and constructor functions are 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'
  }
});
  1. Render this panel to an HTML element with id=content.

  2. Defining a custom config property logo.

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.

Tip
Ed Spencer published a useful list of recommendations on improving performance of Ext JS applications in his blog titled SenchaCon 2013: Ext JS Performance Tips.
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.

Xtype: An Efficient Way to Create Class Instances

One of the interesting pre-processors is xtype, which is an alternative to the invocation of the create() method for creating the instance of the class. Every Ext JS component has assigned and documented 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 4. Each component has an xtype

Using xtype instead of create() leads to more efficient memory management. If the object is declared with the xtype attribute, it won’t be instantiated until some container uses it. You are encouraged to assign xtype to your custom classes, and 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 The 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.
Multiple Inheritance With 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: in these languages 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 The 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 5. 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 The 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. There is no explicit models section, because in our implementation the models were defined inside the stores. 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 The 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 ten 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 Appendix B we discuss 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 can 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. Controller generates getters and setters for each object listed in the refs.

  3. Registering event listeners in the function init(). In this case we’re registering the event handler function showDonateForm that will process clicks on the button, which has an attribute action=showform.

  4. The getter getDonatePanel() will be auto-generated by Ext JS because donatePanel was included in the refs section.

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.

A Component’s Lifecycle

In previous versions of our Save The Child application CSS was responsible for all layouts of the UI components. In Chapter 10 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

  • Initialization of 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.

There’s also the issue of reflows, which happen when the code reads-measures-writes to the DOM and makes dynamic style modifications. Fortunately, Ext JS 4.1 was redesigned to minimize the number of reflows; now a large portion of recalculations is done in a batch before modifying the DOM.

Components as Containers

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. 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.

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 because it could result in duplicate IDs.

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 declaratively subscribe to events using the listeners config property of the component:

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

JavaScript support event bubbling (see Appendix A). 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

The container’s property layout controls how its children are laid out. It does it by referring to the container’s property items, which lists all of the child components. 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"},
   }]
});
Setting Proportional Layouts With 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
 }]
Note
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 The Child application.

Developing Save The Child With Ext JS

In this section we’ll do a code walk-through of the Ext JS version of our Save The Child application. Ext JS framework is often used in the enterprise applications that communicate with Java-based server-side. The most popular IDE among Java enterprise developers is called Eclipse. That’s why we decided to switch from WebStorm to Eclipse IDE for some time. Apache Tomcat is one of the most popular servers among Java developers.

We’ve prepared two separate Eclipse projects:

  • SSC_Top_ExJS contains the code required to render the top portion of the UI

  • SSC_Complete_ExtJS contains the complete version.

To test these applications in Eclipse, you need to install its version titled "Eclipse IDE for Java EE developers" and configure it with Apache Tomcat as described below.

Note: If you are not planning to work with Java servers, you can continue using WebStorm IDE. Just open in WebStorm the WebContent directory from the above project (as you did in the previous chapters) and open the index.html file in the browser. WebStorm will run these Web application using its internal Web server.

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.

Setting Up Eclipse IDE and Apache Tomcat

Although Eclipse is not the best IDE for JavaScript developers, but besides being a super popular IDE among Java developers, we’ll need it to demonstrate the HTML/Java application generation in the next chapter.

Tip
Sencha offers Eclipse plugin (not covered in the book) for those who purchased a license of Sencha Complete.

We’ll use the version "Eclipse IDE for Java EE 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 to start this IDE.

Apache Tomcat

In Chapter 3 we used a XAMPP server that was running PHP scripts. Since this chapter will include server-side code written in Java, we’ll use Apache Tomcat, which is one of the popular servers 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 the next 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 is the latest producation-quality 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. This is an Eclipse pre-configured project that already knows where its Java server located and deployment to the server is greatly simplified. Sample projects from this chapter will be specifically created for deployment under Apache Tomcat server.

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 6. 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 7. Solution 1: 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 8. Solution 2: 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. Developing with ExtJS 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.

Note
If you decided to stick to WebStorm IDE, you can skip Eclipse-related instructions below and just open in browser index.html located in the WebContent directory of the SSC_Top_ExtJS project. In any case the browser will render the page that looks as in Running the SSC_Top_ExtJS below.

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

Save The Child: The Top Portion of UI

To run the top portion of the UI, Select Eclipse menu File | Import | General | Existing projects into workspace and press the button Next. Then select the option Select root directory and press browse to find SSC_Top_ExJS 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 directory SSC_Top_ExJS.

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 configured (deployed), but SSC_Complete_ExtJS isn’t yet.

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 The Child application running in the internal browser of Eclipse that will look as shown on Running the SSC_Top_ExtJS. 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
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

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 SaveThe 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)

    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 7 is dedicated to JavaScript testing, and it contains sections "Testing The Models" and "Testing The Controllers" that illustrate how to arrange for separate testing of the models and controllers in the Ext JS version of the Save The 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 12. 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.

Note: 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.

Tip
Consider using Ext Designer for creating layouts in the wysiwyg mode. .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 The 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 The 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.

Besides SASS, there is another dynamic CSS language called LESS. It adds to CSS variables, mixins, operations and functions It’s not used in Ext JS though.

Now let’s look at the child elements of the SSC.view.Viewport. The SSC.view.Header is the simplest view. Since Save The Child does not include 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 The 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 The Child", and another container with buttons

  5. A container with button components

Let’s review the DonateForm view 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. We’ve removed some of the lines code to make it more readable, but its full version is included in the source code samples accompanying the book.

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)
  },

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

    items: [{
      xtype: 'container',

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

        vertical: true,
        columns: 1,

        defaults: {
          name: 'amount'
        },

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

    items: [{
      xtype: 'textfield',
      name: 'donor',
      emptyText: 'full name'
    }, {
      xtype: 'textfield',
      emptyText: 'email'
    }
    // address,city,zip code,state and country go here
    ]
    }, {
    xtype: 'container',        //  (5)
    layout: {
      type: 'vbox',
      align: 'center'
    },

    items: [{
      xtype: 'label',
      text: 'We accept PayPal payments',
      cls: 'donate-form-label'
    }, {
      xtype: 'component',
      html: 'Your payment will processed securely by PayPal...'
    }, {
      xtype: 'button',
      action: 'donate',
      text: 'DONATE NOW'
    }, {
      xtype: 'button',
      action: 'hideform',
      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 horizontally 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 tip, 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 13. 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 14. DonateForm.js: an hbox with three vbox containers

The code review of the top portion of the Save The 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 The Child application merging the application code into one file to contain only those framework classes that were actually used.

Completing Save The Child

In this section we’ll review the code supporting the lower half of the Save The Child UI, which you should import into Eclipse IDE from the directory SSC_Complete_ExtJS.

image
Figure 15. Save The Child with live charts

If you see the target runtime error, read the beginning of the section "Save The 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 The 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 a snapshot of the Window shown in Save The 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 16. JavaScript classes of SSC_Complete_ExtJS
Adding the Login Box

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();
}
Adding the Video

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, sub-classing 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>'
 ]

});
Tip
Ext JS has a wrapper for the HTML5 <video> tag. It’s called Ext.Video, and we’ll use it in Chapter on Sencha Touch.
Adding the Maps

Adding the map takes considerably more work on our part. 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: {                                 // (2)
  afterrender: function (comp) {
      var map,
          mapDiv = comp.mapContainer.dom;     // (3)

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

 initMap: function (mapDiv) {                   // (5)
   // 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');    // (6)

  campaigns.each(function (campaign) {
      var title       = campaign.get('title'),       // (7)
          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 the map container just by including the HTML <div> component, Ext JS will create generated ID for this <div>. It’s just not a good way to reference an element on the page, since the ID should be unique and we can easily run into conflicting situation. We didn’t want to create an ID manually hence used the property renderSelectors allows to map an arbitrary name to a DOM selector. When we reference this element somewhere inside Ext JS code using this renderSelector, e.g. this.mapContainer(mapContainer is an arbitrary name here), it returns Ext.dom.Element object - an abstraction over the plain HTML element - that eliminates cross-browser API differences.

  2. Sencha documentation states that declaring listeners during Ext.define() is bad practice and doing it during Ext.create() should be preferred. This is an arguable statement. Yes, there is a possibility that the handler function will be created during define() but never used during create(), which will lead to unnecessary creation of the handler’s instance in memory. But the chances are slim. The other consideration is that if listeners are defined during create() each instance can handle the same event differently. We’ll leave it up to you to decide where’s the right place for defining listeners. The good part about keeping listeners in the class definition is that the entire code of the class is located in one place.

  3. 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. Think of comp as this for the component.

  4. 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.

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

  6. 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:

  7. 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 17. 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 2 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.

Later, in the Sencha Touch Chapter, you’ll see another version of our Save The Child application where all stores are inherited from Ext.data.Store. But in this version of our application we are not reading the code from external JSON sources and inheriting from Ext.data.Store would suffice.

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

Adding the Chart and Table Panels

The bottom left area of the Save The 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 18. 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 The Child code review, we need to mention the icons located in the bottom of the ViewPort.js shown on The Viewport footer. Usually links at the bottom of the page statically refer to the corresponding social network’s account. Integration with social networks is out of this book’s scope. But you can study, say Twitter API and implement the functionality to let donors tweet about their donations. The Facebook icon can either have a similar functionality or you may consider implementing automated login to the Save The Child application using OAuth2, which was briefly discussed in the chapter Introduction to Web Application Security.

image
Figure 19. 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 more than three hundred of such tiny images in both vector or PNG form. you’ll see the example of using Pictos fonts in Chapter 12.

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 The 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 The 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 The Child.

image
Figure 20. The size of development version of Save The 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 The 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 downloaded 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 21. The size of production version of Save The Child

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 framework. It’s an extensive framework, which doesn’t allow an easy way out should you decide to switch to another one. But for the enterprise applications that require rich UI, dashboards with fancy charts, advanced data grids Ext JS can be a good choice.