Вы находитесь на странице: 1из 17

HOW TO WRITE BIG APPLICATION IN

Writing a Big Application in Ext (Part 1)

Preface
I have decided to write this article for those users of Ext 2.x that have already grown up from
having one HTML page with embedded script that creates one simple window or form, for
those who are already decided that Ext is the way and for those who are fighting with too
long files hardly to search in and feeling that their applications need a structure.

The number of approaches to a problem and number of solutions of it is equal to number of


people that tackle it. The way I am going to describe in the following text in not the only one
and do not want to say that either an application is going to be written my way or it is not
good. Nothing like that.

What I do want to say is that this approach is workable, neatly structured, easily maintable,
simply stated: It works!

What is “A Big Application”?


If you have a Viewport with BorderLayout, a grid and a form all in one file it certainly is not
the big application, right?. If you have tens of windows each with a grid, form or border
layout in it in tens of files it certainly is the bit application, right?

(Germans have very nice word: Jein which is combination of Ja = Yes and Nein = No.)
The answer to both above statement is Jein. When the application becomes big, then? The
answer is simple: It becomes big when you feel it is big. It is the point when you start to have
troubles to orient yourself in number of files or in you have troubles to find a specific place
in one file, when you cease to understand relations of components, etc. I am writing you here
but imagine when a 2-3 grades less experienced programmer starts to have this feelings.

We can safely state that each application is big as also a small application deserves to be well
written and it may likely become really big as we start to add new features, write new lines
of code, new CSS rules, etc.

The best and the safest state of the mind at the start of a new application is: I’m starting
the big application!

Files and Directories


These we need to organize first. There is always a ServerRoot directory configured in
Apache or another HTTP server so all subdirectories I’ll describe later are relative to it.

Recommended directory structure:


./css (optionally link)
./ext (link)
./img (link)
./js
index.html

Link in the above structure means a soft link pointing to a real directory where files are
stored. The advantage is that you, for example, download new Ext version to a real directory
then you change the link above to point there and without changing a line in your application
you can test if everything works also with this new version. If yes, keep it as it is, if no, you
just change the link back.

• css will hold all your stylesheets. If you have global stylesheets with company colors
or fonts you can create css directory as link too.
• ext link you your Ext JS Library tree as described above
• img link to your images. It can contain icons subdirectory as well.
• js will hold all javascript files the Application is composed of.
• index.html HTML file that is an entry point of your application. You can name it as
you want and you may need some more files for example for a login process. Anyway,
there is one application entry point/file.
• optionally you can create a directory or a link for your server side part of the
application (I have ./classes). You can name it as you wish but consistently for all
applications you write (./server, ./php are some good examples)

index.html
Minimal index.html file content is:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="./ext/resources/css/ext-
all.css">
<link rel="stylesheet" type="text/css" href="./css/application.css">
<script type="text/javascript" src="./ext/adapter/ext/ext-
base.js"></script>
<script type="text/javascript" src="./ext/ext-all-debug.js"></script>
<script type="text/javascript" src="./application.js"></script>
<title>A Big Application</title>
</head>
<body></body>
</html>

Although you can do with the above file I would recommend to add a descriptive header to
not only this file but to all files you create. Also an “End of file” marker has its value. See
File Patterns for example of such headers.

application.js
We need a file where the onReady function will be placed; let’s call it application.js.
Minimum content is:

// vim: sw=4:ts=4:nu:nospell:fdc=4
/**
* An Application
*
* @author Ing. Jozef Sakáloš
* @copyright (c) 2008, by Ing. Jozef Sakáloš
* @date 2. April 2008
* @version $Id$
*
* @license application.js is licensed under the terms of the Open Source
* LGPL 3.0 license. Commercial use is permitted to the extent that the
* code/component(s) do NOT become part of another Open Source or
Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*/

/*global Ext, Application */

Ext.BLANK_IMAGE_URL = './ext/resources/images/default/s.gif';
Ext.ns('Application');

// application main entry point


Ext.onReady(function() {

Ext.QuickTips.init();
// code here

}); // eo function onReady

// eof

Your header and footer may vary but sure you need to set Ext.BLANK_IMAGE_URL to point to
your server. This is path to 1×1px transparent image file that is used by Ext as an image
placeholder and if it points to an invalid location you can get various rendering problems
such as missing combo trigger images, missing icons or similar.

You may also need to create a new global object variable for your application (here it is
Application).

What you need for sure is Ext.onReady that is the main application entry point – the place
where you write your application.

css/application.css
You will put your css stylesheet to this file, if any. If you need only a couple of rules it may
seem as unnecessary to create a separate file for them and it looks like the better idea to put
them to <style> tags in the page head.

The reverse is true, remember you’re writing a big application, so everything has to have its
place. If you put styles in the page head sooner or later you will solve some rendering
problems and you won’t know where the styles are.

The wrong way


What normally follows when we have all basics in as we have at this point? Let’s begin
writing. So we sit down and we start to write:

var vp = new Ext.Viewport({


layout:'border'
,items:[
new Ext.grid.GridPanel({
store:new Ext.data.Store({
proxy:new Ext.data.HttpProxy({ ...

Wait a minute. This way we will have 10,000 lines in our application.js very soon and that is
what we want last. Obviously, some step is missing as if we’re going to create such a big file
why we couldn’t write it in index.html in the first place?

The right way: Break it apart


Even the most complex whole consists of smaller system which consist of smaller parts
which consist of some elements. Your to-be-written big application is not an exception. Now
it is the time to identify this parts, components and relationships between them.

So, sit down, think it over, draw a sketch, make a list, whatever but result has to be that you
know the components, at least most important ones, your application will consist of.

Pre-configured classes
Now, that you are done with application analysis and identifying its components you can
start to write the first one. But how? The best approach is to write extension classes of Ext
components that have all configuration options, otherwise passed as the configuration object,
built-in. I call such extensions pre-configured classes as they rarely add much functionality to
base Ext ones but the main purpose of having them is to have them configured. For example,
to have a “Personnel” grid with personnel specific column model, store, sorting options,
editors, etc.

If we had such, our configuration of a Window could look like:

var win = new Ext.Window({


title:'Personnel'
,widht:600
,height:400
,items:{xtype:'personnelgrid'}
});
win.show();

Writing a pre-configured class


Let’s take an example to discuss:

Application.PersonnelGrid = Ext.extend(Ext.grid.GridPanel, {
border:false
,initComponent:function() {
var config = {
store:new Ext.data.Store({...})
,columns:[{...}, {...}]
,plugins:[...]
,viewConfig:{forceFit:true}
,tbar:[...]
,bbar:[...]
}; // eo config object

// apply config
Ext.apply(this, Ext.apply(this.initialConfig, config));

Application.PersonnelGrid.superclass.initComponent.apply(this,
arguments);
} // eo function initComponent
,onRender:function() {
this.store.load();

Application.PersonnelGrid.superclass.onRender.apply(this,
arguments);
} // eo function onRender
});

Ext.reg('personnelgrid', Application.PersonnelGrid);

What we’re doing here? We’re extending Ext.grid.GridPanel creating the new class
(extension) Application.PersonnelGrid and we are registering it as the new xtype with
name personnelgrid.

We are giving the general grid panel all the configuration options needed to become the
specific personnel grid. From this point on we have a new component, a building block for
our application that we can use everywhere (window, border panel region, standalone) where
the list of personnel is needed. We can create it either as:

var pg = new Application.PersonnelGrid();

// or using it's xtype


var win = new Ext.Window({
items:{xtype:'personnelgrid'}
,....
});

Organizing pre-configured classes


The code above does not need to and should not run within the onReady function because it
has nothing to do with DOM structure; it only creates a new javascript object. Therefore it
can and it should be written in a separate file (js/Application.PersonnelGrid.js) and it
can and must be included in the index.html header as:

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

So far so good, we have almost everything in place and (almost) all we need to do more is to
continue writing our pre-configured classes, put them in ./js; directory, include them in
index.html and build your application from instances of them as puzzle is assembled from
pieces.

Looks good, yeah?

Anyway, there is a bit more to it.

Inter component communication


Imagine that we need a border layout with a link list in the west and tab panel in the center
region. Clicking a link in the west would create a new tab in the center. Now, where should
we put the logic of it, event handler and creation routine? In the west, or in the center?

Neither of them. Why? If we have a pre-configured class that creates and displays the west
link list and we put the above logic in it it can no longer exist without center region. We just
cannot use it without center as then we have no component to create tabs in.

If we put it in center, the result is same: center cannot exist without west.

The only component that should be aware of the existence of both west and center panels is
their container with the border layout and this is the only right place where to put inter-
component communication.

What should we do then? The container (with border layout) should listen to events fired by
the west and it should create tabs in the center as responses to these clicks. The example of
the component communication written this way can be found here: Saki’s Ext Examples

Production Systems
As we keep on writing our application we happen to have large number of included
javascript files very soon (I have around 80 includes in one application at present and this
number grows every day). This can degrade performance of a production system.

The best way to solve it is to concatenate all javascript files in the right order to create one
big and to minify it with some of the javascript minfying or compression tools. Also, you do
not need debug version of Ext Library for production systems.

We would include:

• ext-all.js
• app-all.js and
• application.js

on a production system.

Conclusion
It’s almost all there is to it… There are specific techniques for specific Ext classes, there is a
lot of another server and client side know-how but the above is the overall concept.

Happy coding!
Writing a Big Application in Ext (Part 2)

Important

If you have not already done so, study Writing a Big Application in Ext (Part 1)
before you read this article. It would be very hard, if not impossible, to understand concepts
explained here before you fully understand the first part.

Introduction
It has been almost one year since I have published the first part of this article. I have been
successfully using the concept of pre-configured classes personally to write a really big
application (~150 javascript files, ~60,000 lines of code, plus server-side and css). The
application is fully modularized, each class in separate file and it has proven that it can be
easily developed, managed and debugged.

Unfortunately, the same has not been always true for other users, they were hitting various
problems and Ext Support Team have had to handle may questions and help requests. These
accumulated to the degree where the concept has been called “absolute abomination” (read
absolutely hated) and it was stated that “it causes problems”.

Note: As “fast cars” do not cause accidents but “slow drivers driving fast cars” do, the
concept itself cannot be a cause of the problems but its application can.

In any case, there must be some illogic if I can use the concept but others cannot.

Thus, I have looked deeper in it and I have isolated some pitfalls the users can run into on the
course of development. I will also write on “dos” and “don’ts” and on when to use a pre-
configured class and when not. I will not compare this concept to another application design
concepts (factory functions, in-line configuration, on-demand load, or other) because 1) I do
not use them and 2) I do not want to turn this article into a Linux versus Windows discussion.

It is fully up to you which application design concept you choose. However, if you do choose
this one then follow the rules I’m going to lay down.

Most Common Sources of Troubles


• Dull Copy&Paste
• Extending a non-Ext.Component class
• Not calling parent methods
• initialConfig
• listeners

Dull Copy&Paste

Do you know such people? They post on a forum:

I need to drag from tree to qrid, gimme plz complete codez

And if somebody altruistic writes a fragment of “codez” for him in a sheer attempt to help
the response is going to be:

Your codez don’t work. Help me plz my manager wants it working

Do you see what happened? A dull “developer” ordered a code on the forum, he’s got some,
copied&pasted it to his application without a clue what the code does, maybe hasn’t even
changed url that still points to your server and the result is: it doesn’t work.

Well, this is an extreme (but not so rare as you would think), nevertheless, copying&pasting
without understanding of what the copied&pasted code does can lead only to frustrations.

I am not against Copy&Paste in general, it can save a lot of time and I also occasionally do it,
but I am against not-understanding or mis-understanding not only of coding but also of
life.
The Rule: Do Copy&Paste but always with full understanding of what the code does.

Extending a non-Ext.Component class

If an Ext class does not inherit from Ext.Component the initComponent is never called so
the code you have written there is never executed. This is fragment from Ext.Component
constructor:

1 this.getId();
2 Ext.ComponentMgr.register(this);
3 Ext.Component.superclass.constructor.call(this);
4
5 if(this.baseAction){
6 this.baseAction.addComponent(this);
7 }
8
9 this.initComponent();

Ext classes that do not inherit from Ext.Component do not have this.initComponent();
line in their constructors.

The Rule: Always check if the Ext class you are going to pre-configure inherits from
Ext.Component. You have to use an another approach if it does not.

Not calling parent methods

It happens very often that you do not only add some methods in your extended class but that
you modify existing ones. initComponent being the first example. onRender, afterLayout
are other (but not only) frequently overriden methods.

These methods are already implemented in the class you are extending and its parents so if
you forget the line:

// in initComponent override
YourExtension.superclass.initComponent.apply(this, arguments);

// or in onRender override
YourExtension.superclass.onRender.apply(this, arguments);

// or in afterLayout override
YourExtension.superclass.afterLayout.apply(this, arguments);

your class will not work.

The Rule: Never forget to call the parent method, unless you exactly know what you are
doing.

initialConfig

The constructor of Ext.Component saves the config passed to it as initialConfig:


1 /**
2 * This Component's initial configuration specification. Read-only.
3 * @type Object
4 * @property initialConfig
5 */
6 this.initialConfig = config;
7
8 Ext.apply(this, config);
9 this.addEvents(/* abbreviated for clarity */);
10 this.getId();
11 Ext.ComponentMgr.register(this);
12 Ext.Component.superclass.constructor.call(this);
13
14 if(this.baseAction){
15 this.baseAction.addComponent(this);
16 }
17
18 this.initComponent();

You see what happens? The constructor saves initialConfig before initComponent is
executed. Thus, all configuration you write in initComponent is not saved. I have
overlooked this in first versions of my templates and examples mainly because there is only a
couple of classes that refer to initialConfig and even in these classes the absence of properly
saved initialConfig causes problems very rarely. These Ext classes refer to
initialConfig:

• AnchorLayout
• BorderLayout
• Action
• GridPanel
• Tip
• Combo
• Form

Now, I have updated all my examples, extensions, templates and main site to include this
“magic” pattern:

1 // create config
2 var config = {
3 // put your config here
4 }; // eo config object
5
6 // apply config
7 Ext.apply(this, Ext.apply(this.initialConfig, config));
8
9 // call parent
10 YourExtension.superclass.initComponent.apply(this, arguments);

The Rule: Ensure that your extension saves initialConfig.

listeners

If you try to install event handlers by setting property listeners in your config they will
not work. Why? The answer lies again in the order of actions in Ext.Component constructor:
1 Ext.Component.superclass.constructor.call(this);
2
3 if(this.baseAction){
4 this.baseAction.addComponent(this);
5 }
6
7 this.initComponent();

As you can see, the constructor calls its parent, that is Ext.util.Observable before
initComponent. Ext.util.Observable constructor executes:

1 Ext.util.Observable = function(){
2 if(this.listeners){
3 this.on(this.listeners);
4 delete this.listeners;
5 }
6 };

Any listeners set in initComponent are thus ignored.

There is an easy workaround. Put constructor method in your extension:

1
2 constructor:function(config) {
3 // parent constructor call pre-processing - configure listeners here
4 config = config || {};
5 config.listeners = config.listeners || {};
6 Ext.applyIf(config.listeners, {
7 // add listeners config here
8 });
9
10 // call parent constructor
11 AnExtension.superclass.constructor.call(this, config);
12
13 // parent constructor call post-processing
14
15 } // eo function constructor

and define your listeners therein.

The Rule: Define listeners in constructor method.

Conclusion
If you decide to use my way of writing a big application then follow these rules:

1. Do Copy&Paste but always with full understanding of what the code does.
2. Always check if the Ext class you are going to pre-configure inherits from
Ext.Component. You have to use an another approach if it does not.
3. Never forget to call the parent method, unless you exactly know what you are doing.
4. Ensure that your extension saves initialConfig.
5. Define listeners in constructor method.
Happy extending!

Writing a Big Application in Ext (Part 3)

Important

If you have not already done so, study Writing a Big Application in Ext (Part 1)
and Writing a Big Application in Ext (Part 2)before you read this article. It would
be very hard, if not impossible, to understand concepts explained here before you fully
understand the first and second part.

Introduction
Helping on the forum and reading code of others that failed to extend Ext classes, revealed
more errors that users, especially beginners, commonly make. Therefore, I’ve decided to start
this article that will collect these errors and will explain why the errors are errors. I mean it
as loosely ended as I may discover more errors and ways of avoiding them so I plan just to
add them to this article, not endlessly create parts 4, 5, etc…

… continued: Most Common Sources of Troubles


Here we go:

1. Unnecessary Extending
2. Adding Objects to Prototype
3. Hard Coding ids
Unnecessary Extending
The main reasons for extending are:

• re-usability
• adding functionality
• combination of them

so we extend if we need a re-usable component or we need to add a functionality (new


methods) or both. If we are after re-usability the extension can be as simple as:

1 MyPortlet = Ext.extend(Ext.Panel, {
2 anchor:'100%'
3 ,draggable:true
4 ,defaultType:'mygraph'
5 });

You see what happens? We are going to use MyPortlet many times so instead of scatter the
above configuration in 10,000 lines application code 100 times, we create this simple
extension and we save 297 lines of code.

The other aspect is that if we upgrade our ‘mygraph’ to ‘mygraph_new’ the only place where
to change it is our extension saving us searching out code for all occurrences of ‘mygraph’
(100 times) and replacing it with ‘mygraph_new’ 100 times.

(Well, 100 is exaggerated, but you get the point, right?)

If we are after adding functionality, which can be also simple, we add some “logic”:

1 MyPanel = Ext.extend(Ext.Panel, {
2 onRender:function() {
3 MyPanel.superclass.onRender.apply(this, arguments);
4 alert('Rendered');
5 }
6 });

Here we add some logic to Panel, it does more that it did before.

There is no need to extend in all other cases.

Adding Objects to Prototype


Run this code first:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="ext/resources/css/ext-
all.css">
<script type="text/javascript" src="ext/adapter/ext/ext-
base.js"></script>
<script type="text/javascript" src="ext/ext-all-debug.js"></script>
<title id="page-title">Extending Error: Object in prototype</title>
<script type="text/javascript">
Ext.BLANK_IMAGE_URL = 'ext/resources/images/default/s.gif';
Ext.onReady(function() {
MyPanel = Ext.extend(Ext.Panel, {
layout:'fit'
,panelConfig: {
bodyBg:'red'
}

,initComponent:function() {
var config = {
bodyStyle:'background-color:' + this.panelConfig.bodyBg
}; // eo config object

// apply config
Ext.apply(this, Ext.apply(this.initialConfig, config));

MyPanel.superclass.initComponent.apply(this, arguments);
} // eo function initComponent

,applyBackground:function(color) {
this.panelConfig.bodyBg = color;
this.body.applyStyles({'background-color':color});
} // eo function applyBackground

}); // eo extend

var p1 = new MyPanel({


title:'Panel with Blue Background'
,renderTo:Ext.getBody()
,width:240
,height:160
});

p1.applyBackground('blue');

var p2 = new MyPanel({


title:'Panel with Red Background'
,renderTo:Ext.getBody()
,width:240
,height:160
});

});
</script>
</head>
<body></body>
</html>

What do we expect? It is written in titles of panels: Top panel (p1) should have blue body
background because we set it to it after it is created. And bottom panel (p2) should have red
because we just create default MyPanel.

But it is blue too!!! Why? The reason is simple: panelConfig is object that is created
during class definition and it is added to MyPanel prototype. Objects (arrays too) are
accessed by reference so p1 and p2 share the same instance of panelConfig. Setting
bodyBg property in applyBackground method changes this single instance of panelConfig
object. So we create p2 with blue background too.
You see, here it is clearly and immediately visible that something went wrong but making
this error can lead to weeks of wasted debugging time in real applications. Imagine you have
a store in prototype…

Hard Coding ids

Very simple, but deadly mistake is to set ids in the extension either to the main extension
object or on its items, toolbars, buttons, etc. If a hard coded ids are set we cannot create two
or more instances of our extension, can we?

Loose End
That’s all for now but if I discover more errors I will add them above.

Stay tuned!

Вам также может понравиться