PDA

View Full Version : How to extend a Ext.app.Application?



myExtJsUname
22 Aug 2012, 10:38 AM
Actually, I'm not even sure if extending Ext.app.Application is what I need to do. I've been reading the Getting Started and Class concepts as well as searching around the Forums, but haven't really found anyone doing what I'm trying to do...or maybe I have and I just haven't recognized it. Really, I'm just looking for some clues regarding a general approach I should be taking here. I don't mind reading the docs to figure out details, but doing so hasn't allowed me to glean even a general approach to pursue so far and I can't even come up with something to begin attempting.

Basically, I want to set up an ExtJs "application" structure where I have a base "application" class from which I extend all my other "applications" (my use of the term "application" here should not necessarily be construed to be the exact same as an Ext.app.Application. It might be, but I'm not sure). Below are a couple of examples trying to illustrate what I have in mind. Neither of them are legitimate examples, I realize, but I'm hoping they will convey the crux of what I'm trying to do.

Any clues as to how to approach this are appreciated; or an indication if it is not possible would be great before my brain disintegrates trying to figure it out.

Cheers,
jtm


Example 1 - Do I set the base stuff in the Ext.application() and then extend that somehow? This won't work because I have to point to the ExtendedApp from the HTML, which means there is no "Ext.application()" to initiate everything when Ext is ready.

BaseApp.js

Ext.application({
name: "BaseApp"
,launch: function(){
alert("BaseApp.launch");
}
})

ExtendedApp.js

var app = null;
Ext.define(
"ExtendedApp"
,{
extend: "BaseApp"
,launch: function(){
parent.launch(); // meaning that I want to run the code found in the BaseApp's launch function
alert("ExtendedApp.launch");
app = this;
}
}
);

HTML

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


Example 2 - Do I put the Base stuff in some kind of extension to Ext.app.Application and then instantiate that somehow from within the ExtendedApp? This can't be right because then I've got 2 Ext applications, one contained within the other.

BaseApp.js

Ext.define(
"BaseApp"
,{
extend: "Ext.app.Application"
,launch: function(){
alert("BaseApp.launch");
}
}
);

ExtendedApp.js

var app = null;
Ext.application({
name: "ExtendedApp"
,{
baseObj: null
,launch: function(){
baseObj = Ext.create("BaseApp");
baseObj.launch();
alert("ExtendedApp.launch");
app = this;
}
}
});

HTML

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

myExtJsUname
22 Aug 2012, 10:51 AM
I may have stumbled across something at: http://stackoverflow.com/questions/10617879/extjs-nested-apps which further directed me to: https://github.com/mitchellsimoens/SubAppDemo. I've downloaded that and am starting to look into it now.

mberrie
22 Aug 2012, 9:53 PM
If your goal is only to sub-class Ext.app.Application then this is the way to go...

In order to have full control over the application you might want to check out the code for Ext.application(). It's actually VERY simple:


Ext.application = function(config) {
Ext.require('Ext.app.Application');

Ext.onReady(function() {
new Ext.app.Application(config);
});
};


It should be obvious how to facilitate your own Application class (that sub-classes Ext.app.Application) - just ditch Ext.application for three lines that create your own Application instance in Ext.onReady.


I am not sure how much experience you have with ExtJs. If this is your first project, I recommend to follow Ext's development model first (one application object, register controllers, stores and models on the application object) to gather experience and not get lost.

Once you understand how the MVC framework, the class system, and the application live-cycle are designed to work based on a real-world application scenario, you can look further into intoducing a more modular architecture.


Application Architecture

If you need more than that and want to look into how to architect more complex applications, here are a view starters:

Links
http://www.sencha.com/forum/showthread.php?133087-Multiple-Ext.Applications-(Big-Applications)
https://github.com/Fredric/ExtJS-Multiple-MVC
http://www.sencha.com/forum/showthread.php?129236-Best-practice-for-multiple-Application-objects-on-one-page
https://github.com/mitchellsimoens/SubAppDemo

EventBus Singleton (no more)
Sencha already made one significant change in 4.1 related to application architecture. The EventBus is no longer a singleton, but each application instance will create its own EventBus instance.

EventBus and UI component events
However, it is noteworthy that each EventBus will listen to events fired by *all* ui components:

EventBus.js:


constructor: function() {
this.mixins.observable.constructor.call(this);

this.bus = {};

var me = this;
Ext.override(Ext.Component, {
fireEvent: function(ev) {
if (Ext.util.Observable.prototype.fireEvent.apply(this, arguments) !== false) {
return me.dispatch.call(me, ev, this, arguments);
}
return false;
}
});
}


Dissect Ext's Application class
Understand how application objects (controllers, stores, models) are instantiated when registering them on the application object. There is not that much magic going on. Check out the #getController(), #getStore() etc methods.

Understand how to instantiate a controller class and register the object with the application programmatically.

EventBus.uncontrol
Once you create Controllers programmatically you probably want to destroy them again when you no longer need them. Ext's application life-cycle model expects controllers to exist 'forever'.
You will need your own EventBus.uncontrol method which unregisters the controller's event listeners on the EventBus.
I think Mitchell has an implementation in the SubApp demo on github.

myExtJsUname
23 Aug 2012, 11:36 AM
mberrie,

Thank you for your response and the time you obviously spent putting it all together. I have tried to work through the threads you referenced and Mitchell Simoens "SubAppDemo" but have to admit that much of it is beyond me. Really many pieces of information are the same ones I've been trying to cobble an understanding of in the last week to no avail.

Having said that (and realizing you may decide my questions are not worth your time; in which case, I'll thank you for all you have already done), I've decided to just focus on the initial portion of the response:

If your goal is only to sub-class Ext.app.Application then this is the way to go...

In order to have full control over the application you might want to check out the code for Ext.application(). It's actually VERY simple:

Ext.application = function(config) {
Ext.require('Ext.app.Application');

Ext.onReady(function() {
new Ext.app.Application(config);
});
};

It should be obvious how to facilitate your own Application class (that sub-classes Ext.app.Application) - just ditch Ext.application for three lines that create your own Application instance in Ext.onReady.



Hmmm, sorry, it's not so obvious to me (again, no worries if you quit now).

Here's what I tried (well, I've tried many things over the last few days, but this is me trying to do the simplest possible thing trying to figure out how it works):

Ext.define(
"TestApp"
,{
extend: "Ext.app.Application"
,name: "TestApp"
}
);
var app = Ext.create("TestApp");
Ext.onReady(app);
The 'Ext.create("TestApp")' line causes an error that "TypeError: c is not a constructor". So, I tried adding in a constructor (among other things, like initialize, init, initComponent, really anything that looked promising), but it made no difference.

Ext.define(
"TestApp"
,{
extend: "Ext.app.Application"
,name: "TestApp"
,constructor: function(config){
alert("TestApp constructor");
this.callParent(arguments);
}
}
);
var app = Ext.create("TestApp");
Ext.onReady(app);
What I have managed to do is define a class, extend it and instantiate the extended class but they are my own classes and don't extend anything in ExtJs. As follows:

Ext.define(
"TestApp"
,{
toolbar: null
,map: null

,constructor: function(config){
alert("TestApp constructor");
}

,load: function(){
alert("TestApp load")
}
}
);

Ext.define(
"ExtendedApp"
,{
extend: "TestApp"

,constructor: function(config){
alert("ExtendedApp constructor");
this.callParent(arguments);
}

,load: function(){
alert("ExtendedApp load")
this.callParent(arguments);
}
}
);

//var app = new ExtendedApp();
var app = Ext.create("ExtendedApp");
Ext.onReady(app.load, app);
So, I still don't actually have an Ext.app.Application type of object at the end of this. Is there something really obvious I'm missing in my attempt to extend "Ext.app.Application" above?


I'm using ExtJs 4.1.1, if it matters.

Cheers,
jtm

myExtJsUname
23 Aug 2012, 1:03 PM
Okay, wait a second...given the following:

<html>
<head>
<link rel="stylesheet" type="text/css" href="/eMAF_ExtJs/resources/css/ext-all.css">
<script type="text/javascript" src="/eMAF_ExtJs/ext-debug.js"></script>
<script type="text/javascript">
Ext.define(
"TestApp"
,{
extend: "Ext.app.Application"
,launch: function(){
alert("TestApp launch");
}
}
);
//alert("hello");
var app = Ext.create("TestApp");
Ext.onReady(app);
</script>
</head>
<body>
</body>
</html>

I get the "TypeError: c is not a constructor" error I mentioned in a previous post. But, if I uncomment that "alert("hello")" line, I will see the "hello" and the "TestApp launch" alerts and will get a "handler.apply is not a function" error instead.

If I re-comment the alert("hello") line, it fails with the constructor error again. What the heck is that about?

I'm using Firefox; I've cleared my cache; I've even rebooted. I'm using Ctrl-F5 to refresh the page. Can't think of anything else to tell you that might be useful information.

Later edit: I just tried it in IE and it fails with the constructor problem whether the alert is there or not.

Cheers,
jtm

mberrie
23 Aug 2012, 8:15 PM
Almost! :)




Ext.define(
"TestApp"
,{
extend: "Ext.app.Application"
,name: "TestApp"
,constructor: function(config){
alert("TestApp constructor");
this.callParent(arguments);
}
}
);

Ext.onReady(function() {
var app = Ext.create("TestApp");
});



Some hints:

Ext.define loads your class *definition*. No *instance* (object) of your class is created yet. The constructor is *not* executed yet.

Ext.onReady() allows you to define a callback function that will be executed once the page and the framework is done with initializiation stuff.


This is what happens when you run your code

1. Ext.define loads your class definition into the Ext.ClassManager. This will trigger loading of dependencies e.g. Ext.app.Application which your class extends.
2. the Ext.onReady line is executed and the callback is *registered*, but the callback function itself is not executed.
3. Once the document is loaded and Ext has finished its initialization tasks, Ext will *execute* your callback. This means that
4. Ext.create("TestApp") will create a new *instance* of the TestApp class, which means the constructor is *executed*. Of course, this means also that the parent constructor in Ext.app.Application is executed as well. If you look at the code there you will see that *a lot* happens. It will also trigger the 'launch' function on the way.
5. Ext.app.Application will also try to create a 'Viewport' if autoCreateViewport is true. Viewport is a UI component so if that works out, you should actually *see* something in the Browser :). For autoCreateViewport to work you need to get the class names right. As a starter, you could set autoCreateViewport to false, and create a Window class in your 'launch' callback.



// as a member of your TestApp class
,launch: function() {
this.callParent(arguments);
var win = Ext.create('Ext.window.Window', {
width: 400,
height: 400
});
win.show();
}



Disclaimer

May I ask why you actually want to extend the application class? If you follow the standard application pattern and look at other examples, you will see that for smaller apps it is hardly ever necessary to do that. Usually you just use Ext.application() to configure the application object. Then you can either create a Viewport class in 'AppName.view.Viewport' (with AppName matching your configured 'name' property on the application object) or override the 'launch' function to create any UI component you need.


Also, I recommend getting familiar with JavaScript debugging ASAP. Use Firefox/Firebug or better (my opinion) Chrome/Developer Tools. Set a breakpoint at key points in your code and in Ext.app.Application, then run from breakpoint to beakpoint (F8) and you will see the execution order of things.

myExtJsUname
24 Aug 2012, 8:00 AM
mbarrie,

Again, thank you for your response.

First, the easy part...I see the difference between your code and mine. Essentially, as I understand it, mine was trying to instantiate an object from the definition before ExtJs was actually capable of creating it. Hence the wierd behaviour I was seeing; the appearance of the alert was actually giving ExtJs time to finish loading up. Your approach ensures ExtJs is "ready" to go to work before asking it to actually "do" anything. And, I understand what you're discussing in terms of the autoCreateViewport stuff. So, changing my code and moving the instantiation inside the Ext.onReady callback allows my extended application to be created, executed, etc.

Second, the "Disclaimer" part...I am fully cognizant that I am setting this up wrong but I'm having trouble translating the documentation I have been reading into what I am ultimately trying to do. I have a feeling that what I'm really trying to get to are a number of different controllers but, again, I can't really "see" how what I'm trying to do fits in to the suggested structure.

In as simple terms as I can describe them, I have a number of "applications" (really "web pages") that will all have only two main components displayed: a map and a toolbar. Each "application/page" will have a different set of data displayed in the map object (provided by another third-party library) and a different set of tools available in the toolbar (although a couple tools will be common to all). I want a "base" class that lays out the viewport with those two components added in a standard way and that has some shared functionality (e.g, how the map DIV should be resized when the browser is resized and the functionality to add a couple of tools to the toolbar that all "application/pages" should have access to). Then, each "extension" will add its "application/page"-specific data to the map and tools to the toolbar.

To set your mind at ease, at this point I have already figured out all the code to do this for a single "application/page" and have a running example, but it's pretty much all defined within one HTML and one additional JavaScript file. What I'm struggling with now is how to set up an "architecture" properly so it's easy to create other similar "applications/pages".

When I look at the MVC Architecture guide's suggested File Structure, I don't understand how I can only have a single index.html/app.js for this; unless perhaps I am passing a parameter? We'd really prefer not to do that because we'd like the HTML metadata to reflect the type of information that will be displayed in the map and, so they can be added to search engine lists, we don't want that to be dynamic. So, given all that, I believe we need at least an HTML per "application/page". But, obviously, we don't want to include the extjs library repeatedly for each, so we wouldn't want to create an "account_manager" directories (using the File Structure from the Guide as reference) for each. Instead, we'd rather create a single "account_manager"directory, but multiple HTML/app.js...perhaps...?

As I said above, it's possible that what I'm really describing is different controllers sharing a standard view but I'm having difficulty translating the documentation into my particular problem. I think, too, that if that is correct I would need different app.js files as well as different HTML; assuming that is where the Ext.application() part is as it appears to be suggested. Arguably, that could just be put in the HTML and the applicable controller provided...am I right? I don't really know.

So, am I expected to do something like (again, using the File Structure from the Guide as reference and recognizing there are many ways to skin a cat so there is no single definitive "correct" way to do this):

- define app.view.MySharedView (which extends "Ext.container.Viewport" and adds the map and toolbar components, adds a couple shared tools, and provides the browser resize functionality)
- define app.controller.MapPage1, app.controller.MapPage2, etc. where the specific pieces of information I need to display in each map is defined and the "map-specific" tools added to the toolbar
- at the same level as the "app" directory, have a MapPage1.html (for example) that has script that does something like this:

Ext.application({
requires: ["app.view.MySharedViewport"]
,appFolder: "app"
,controllers:[
"app.controller.MapPage1"
]
,launch: function(){
Ext.create("app.view.MySharedViewport");
}
:
:
});
So, then, if someone needs to add a new "application/page" showing different map data and providing different tools, they will simply define a new "app.controller.MapPage*" and create a new HTML page where the only thing they will change is which controller to use?

I think I'm blurring the controller/view thing though. I'm adding what seems like controller stuff (resize) to a view. Besides that, it's not clear to me how the controller stuff gets "started" but I haven't been able to get that far to try it and see how it fits/works together so I'm not really concerned that I don't get that yet.

Am I instead supposed to be creating "app.controller.BaseController" to extend "Ext.app.Controller" which does all the stuff I described in the view above? And, then, further extend that with the individual "application/page" controllers?

This is where I get lost and, unfortunately, have no one to discuss it with here. So, if you've made it this far without giving up and care to comment, I'd be happy to hear it. At the moment, I can visualize how to do this by extending the Ext.app.Application to create a BaseApp that does what I describe in the view above and then create an ExtendedApp whose launch does the "application/page-specific" loading. To create a new "application/page", I would have to create the new ExtendedApp and a new HTML that instantiates and launches it when Ext is ready.

I'll keep trying to work through the view/controller ideas I discussed above in the hopes that doing so will give me a better understanding of how this stuff is working and is put together. I find that each time I try a different approach another paragraph or two in the documentation starts to make sense.

Again, thank you for your time thus far and don't feel concerned if you are unable or disinclined to continue.

Cheers,
jtm

(Later edit: There's a naming/packaging convention the ExtJs is assuming that's messing me up here and appears to be the reason...well, one of many, I'm sure...why I can't seem to get any of these "architectures" I'm trying to investigate to work. It seems to be taking the current location of a js that defines a class and look below that for any "Ext.require"d stuff in it, but I can't figure out how to make the ExtendedApp get found and then look above that directory and in another directory for the BaseApp. God this is painful!

Even later: okay, I found the documentation that tells me about this "path/to/src is the directory of your application's classes. All classes should stay under this common root..." So, now I really don't have a clue how I'm supposed to go about this.)

mberrie
24 Aug 2012, 10:57 AM
re: disclaimer. I was just worried that you try to map out your application architecture before you have a good understanding of the standard architecture that Sencha has mapped out for us. I am glad you didn't get it the wrong way.

Actually, as you describe your requirements and discuss your ideas, I can see that you are ready to make the next step :)

I can't provide you a definitive solution or roadmap, but I think I can give you feedback that might help.

I understand that you have these pages with different maps. How interdependent (or independent from each other) are they. Is there any navigation from one page to another. If so, what do you want the user experience to be?
If you go with individual pages (navigation reloads the entire page/html document from the server) user experience might not be as smooth and responsive as in a single-page app setup.
Search engine support is possible in a single-page app as well but requires more effort.

It sounds reasonable to me that individual html documents are a better match for your requirements.

I don't think that the file size and loading time of your Ext Javascript files are an issue here. So let's assume that we package the Ext code for all pages into one single (minified) app.js file.
Individual packaging (for each page, only containing the class files for that specific page) is surely possible but let's discuss that later if you really need it.

I can see from the code snippet that you posted (Ext.application...) that you understand now how you could launch a customized app that loads a specific viewport class and controllers.

As you suggest a page specific controllers could be a sub-class of a common base controller class. That sounds very reasonable to me. It might as well be suitable at one point that the controller for one page extends another page controller (instead of extending the base controller directly) if they share some functionality.
Another option is that you register two (or more) controllers - the BaseController plus a page specific controller. Or any variation of these two patterns.
This is merely a question of structuring your code base in order for controller classes not becoming to huge.

This is basically the setup you describe in your 1. 2. 3. paragraph.





So, then, if someone needs to add a new "application/page" showing different map data and providing different tools, they will simply define a new "app.controller.MapPage*" and create a new HTML page where the only thing they will change is which controller to use?

That depends on how your pages differ from each other, but basically yes. But it is up to you to subclass any of your base classes in order to add the behavior/functionality you need for this specific page.
You can then build your Viewport in the launch function (e.g. adding different components or differently configured components as child items).

You could also move the view setup from the Application#launch function into your page specific controller.



launch: function() {
this.getController('myapp.controller.MyPageController').show();
}


Then create the Viewport or whatever in the controller's show method.




I think I'm blurring the controller/view thing though. I'm adding what seems like controller stuff (resize) to a view. Besides that, it's not clear to me how the controller stuff gets "started" but I haven't been able to get that far to try it and see how it fits/works together so I'm not really concerned that I don't get that yet.


In the Ext framework there is a lot of event handling going on in the UI classes. The whole framework doesn't define any single controller class. That has legacy and other reasons.

So it's not entirely wrong to handle events in the UI classes, especially with an entirely UI related event like 'resize'.



Am I instead supposed to be creating "app.controller.BaseController" to extend "Ext.app.Controller" which does all the stuff I described in the view above? And, then, further extend that with the individual "application/page" controllers?

Yes, why not? If your pages share common controller functionality, then do it!



(Later edit: There's a naming/packaging convention the ExtJs is assuming that's messing me up here and appears to be the reason...well, one of many, I'm sure...why I can't seem to get any of these "architectures" I'm trying to investigate to work. It seems to be taking the current location of a js that defines a class and look below that for any "Ext.require"d stuff in it, but I can't figure out how to make the ExtendedApp get found and then look above that directory and in another directory for the BaseApp. God this is painful!

Even later: okay, I found the documentation that tells me about this "path/to/src is the directory of your application's classes. All classes should stay under this common root..." So, now I really don't have a clue how I'm supposed to go about this.)

There is a lot of 'coding by convention' auto-magic going on that makes things unnecessary completed IMHO.

I am not exactly sure into which problems you ran exactly. So some pointers.

Don't change the 'name' of your individual apps, have them all use the same name. That name equals the subdirectory where Ext will be looking for your class files.

Also have a look at Ext.Loader#setPath and Application#paths.

And yes, each class should define its dependencies via 'uses' or 'requires' (use the first for now, it is the better match in 99% of the cases).http://docs.sencha.com/ext-js/4-1/#!/api/Ext.Class-cfg-uses (http://docs.sencha.com/ext-js/4-1/#%21/api/Ext.Class-cfg-uses)

The dependencies are really important later when you package your classes into one single app.js file. For now I recommend that you work with ext-dev.js and have the classes loaded via Ext.Loader. Let's look at the packaging later.

I also recommend to use full qualified class names everywhere (e,g. myapp.view.MyView). Ext supports using the class name in some situations when working with controllers, views, models, etc and it will add the namespace for you (e.g. myapp.controller.XXX). But I found this to be really confusing.

myExtJsUname
24 Aug 2012, 11:33 AM
Oh, sorry, while I was writing this you posted something else. Let me read that first...

Well, I have something working, but it means that I'll have dozens of HTML files under the "appname" directory at the same level as the "app" directory (where all the JavaScript classes are defined). I really didn't want to have that top level get so messy.

What I have now is:

appname
/app
- BaseApp.js
- ExtendedApp1.js
- ExtendedApp2.js
/extjs
/resources
/css
/images
- ExtendedApp1_eng.html
- ExtendedApp1_fre.html
- ExtendedApp2_eng.html
- ExtendedApp2_fre.html

And in ExtendedApp1_eng.html, for example, I have:


Ext.onReady(
function(){
var app = Ext.create("app.ExtendedApp1");
}
);

Really, what I'd prefer is a directory structure something like this:


appname
/app
- BaseApp.js
/apps
/ExtendedApp1
- index_eng.html
- index_fre.html
- ExtendedApp1.js
/ExtendedApp2
- index_eng.html
- index_fre.html
- ExtendedApp2.js
/extjs
/resources
/css
/images


And in the /apps/ExtendedApp1/index_eng.html, for example, have:


Ext.onReady(
function(){
var app = Ext.create("ExtendedApp1");
}
);

And the ExtendedApp1.js extend "app.BaseApp".

I thought the appFolder could be used to do this in some way, but so far I haven't been able to figure out how. When it recognizes that it needs the app.BaseApp definition to extend the ExtendedApp1 class, it looks for it at /apps/ExtendedApp1/app/BaseApp.js. I tried setting the appFolder in "ExtendedApp1" to "/app/" (among other things) but that didn't work. I also tried messing about with setting up a namespace in the hopes that it would register "app.BaseApp" in someway that would allow it to be found by "ExtendedApp1" and loaded the script for BaseApp.js directly in the HTML. Also no go; but then I'm just grasping at straws at this point.

Anyway, even if I figured out how to set this all up using views and controllers, etc. rather than extending the Ext.app.Application, I think I'm still going to have the same problem...that is, many HTMLs will have to be located at the same level as the /app directory for it to find the class definitions referenced in the Ext.application() call. That is, if in the HTML, I used Ext.application() and added a controller "app.controller.ExtendApp1", there's no way to tell it to look for that class from some directory level other than where the HTML is. Is there a way to do this that I just am not seeing somewhere?

I have such a headache right now.

Cheers,
jtm

myExtJsUname
24 Aug 2012, 12:46 PM
mbarrie,

I wanted to respond before the weekend, because I won't have access to what I'm working on again until Monday and I wanted to thank you for the pointers and additional info you provided.

Again, first the easy part...each "application/page" is discrete. There is no requirement to communicate or navigate between them; loading one HTML is expected to load all that's necessary to work within that page. Really, all I'm trying to do is create a bunch of shared code that works with the map object (the same way in every "application/page") and provides a bunch of tools that will work on that map object (again, the same way in every "application/page"). The only difference between each "application/page" is what layers of information are displayed in the map and which tools are available given those specific layers of data. For example, one map might display soil information and another might display hydrology information. Both will require a tool that will display a legend in a Panel and the code to build that legend will interact with the map object in the same way to build its contents no matter what the map content itself is, so that will be a shared tool and the code for it located in a shared location. In addition, however, the hydrology "application/page" may have another tool available that allows the user to click on the map and trace the downstream flow of water from that point. That tool is not applicable to the soil "application/page" and wouldn't be included there. Again, the individual pieces of code to do all this (create panels for the tools, interacting with the map, etc.) are all already figured out. It's the overall way of setting it up that I'm struggling with.

As you suggest, there is no problem with loading all the shared code, even if only certain portions are ultimately made accessible given the tools added to the "application/page". However, I'd prefer not to load, as in the example above, all the code for doing water traceability if the user is only looking at the soils page. I'll worry more about that detail later, but it's a "nice to have" rather than necessary.

Finally, I'm okay with your comments and the discussion re: extending views/controllers instead of the entire application itself. And, in theory anyway (I think...hope), understand how I might break all that up in the subdirectories and access/use them from that angle rather than extending the Ext.app.Application.

So...your pointers and the problem I'm struggling with now as more fully described in that post that I wrote while you were answering my previous post. To summarize it, I don't want to have dozens (hundreds) of HTML files directly under "appname" at the same level as "app" (the javascript source), but would prefer to place them in a sub-directory and tell Ext where to find the class definitions to work with.

1. Upon a quick perusal, the Ext.Loader#setPath and Application#paths looks very promising and may be exactly what I was asking for in that other post. I'll look into that more now.
2. Thanks for highlighting the "uses" keyword for the Class definitions. I've never noticed it before. So far, I've only been using the "requires" but I'll look into the differences more and make changes as necessary.
3. I'm not sure I understand what you're suggesting with your first pointer "Don't change the 'name' of your individual apps...". Does this mean I should define ExtendedApp1 and ExtendedApp2 with the same name? For example:


Ext.define(
"ExtendedApp1"
,{
extend: "app.BaseApp"
,name: "CurrentApp"
:
}
);

Ext.define(
"ExtendedApp2"
,{
extend: "app.BaseApp"
,name: "CurrentApp"
:
}
);
I'm not really sure that I understand the implications of that and its relationship to the subdirectory Ext is looking for my class files, but I'm hoping that as I get further along, I'll make more sense of it. I mention this here, not in the hopes that you will provide further clarification at this point but so that, if I still don't get it later after looking into this and the above information more, you won't be surprised if I find I have to ask for more later.

Thank you so much for all this. My headache's feeling better already...at a minimum I have more things to read and try that will, hopefully, lead to me figuring this out.

Have a good weekend!

cheers,
jtm

mberrie
24 Aug 2012, 9:04 PM
I think you are really close to a solution now. The last piece that is missing is the correct configuration of Ext.Loader. Let's have a look...

This piece of code goes right before your 'Ext.application(..)' snippet.



Ext.Loader.setConfig({
paths: {
'Ext': '../../extjs/src', // match classnames like Ext.*
'common': '../../common', // match classnames like common.*
'app1': 'src' // match classnames like app1.*
},
enabled: true
});


This would match a directory structure like this:



ROOT
/common
/controller
- Toolbar [classname: common.controller.Toolbar, extend: Ext.controller.Controller]
- BaseController [classname: common.controller.BaseController, extend: Ext.controller.Controller]
/app
- BaseApp.js [classname: common.app.BaseApp, extend: Ext.app.Application]
/apps
/ExtendedApp1
- index_eng.html
- index_fre.html
/src
- ExtendedApp1.js [classname: app1.ExtendedApp, extend: common.app.BaseApp]
/controller/
- App1Controller.js [classname: app1.controller.App1Controller, extend: common.controller.BaseController]
/view
- SoilPanel.js [classname: app1.controller.view.SoilPanel, extend: Ext.panel.Panel]
/ExtendedApp2
- index_eng.html
- index_fre.html
- ExtendedApp2.js
/extjs
/src
/resources
/css
/images


The Ext.Loader path config is straight forward, there is not much magic going on there. It simply maps a namespace to a certain filesystem/url path.

Instead of having classnames like 'common.app.BaseApp' and 'app1.controller.App1Controller' you could also go with 'mymap.common.app.BaseApp' and 'mymap.app1.controller.App1Controller'.

the mapping would look like this:



paths: {
'mymap.common': '../../common',
'mymap.app1': 'src'
}


Also, I introduced a /ExtendedAppX /src sub directory. This is not a requirement. If you like, you can keep the JS class files right inside the /ExtendedAppX directory, just adjust the path to '.' instead of 'src'.

I think you get the hang of it. Make sure you read the Ext.Loader and Ext.Loader#setPath docs.



What might spoil that neat concept is a nasty behavior of Ext.app.Application class. The default behavior will ALWAYS overwrite the path mapping when it fires up:

Have a look at Ext.app.Application.js:134:



Ext.Loader.setPath(me.name, me.appFolder);


That is the 'name' property that I was referring to in a previous post. If you have a configuration like yours:



Ext.define(
"ExtendedApp2"
,{
extend: "app.BaseApp"
,name: "CurrentApp"
//default value for appFolder is 'app', see Ext.app.Application.js
}
);


it will create a path mapping of 'CurrentApp' : 'app' . This will override any configuration we might have set before.

Your application's 'name' property has also another significant implication. It is used by Ext to do some auto-magic when dealing with application objects like controllers, views, etc.

A call to Application#getController('App1Controller') will resolve to a class name of 'CurrentApp.controller.App1Controller'.

I recommend to always use full qualified class names, e.g. getController('CurrentApp.controller.App1Controller').

It should make things more obvious and readable especially in a setup like yours.

Still, it probably doesn't hurt to configure your sub apps with

name: 'app1',
appFolder: 'src'

(this will reapply the corresponding mapping to the Ext.Loader config and do no further harm.)


If your app doesn't load make sure you watch the console output and/or the 'network' tab of your developer tools (Firefox, Chrome). If you've got the configuration wrong and it cannot load a specific class/file, Ext will tell you which file path it tried to load!

Also, make sure that your class names in Ext.define('XXX') always match the file name and directory path. This is a common source for errors as well.

myExtJsUname
27 Aug 2012, 11:47 AM
Yay!!

Thanks to all the help and pointers provided by mberrie, I have been able to create a BaseApp which extends Ext.app.Application and then further extend my own BaseApp with ExtendedApp's as discussed in this thread. At the moment, it's just the basics but at least it's working.

So, in case anyone else is going through this trying to do something similar...

At the moment, I have the following structure:


/distdir
/apps
/ExtendedApp1 [e.g., Water]
- index_en.html
- index_fr.html
- ExtendedApp1.js [e.g., Water extends my.BaseApp]
/ExtendedApp2 [e.g., Soils]
- index_en.html
- index_fr.html
- ExtendedApp2.js [e.g., Soils extends my.BaseApp]
/src
- BaseApp.js [my.BaseApp extends Ext.app.Application]
/tools
/watertraceability
- Window.js [my.tools.watertraceability.Window extends Ext.window.Window]
/legend
- Window.js [my.tools.legend.Window extends Ext.window.Window]
/resources
/css
/ext-4.1.1
/images


In /distdir/src/BaseApp.js, I have the following:


Ext.define(
"my.BaseApp"
,{
extend: "Ext.app.Application"
,name: "my.BaseApp"
,uses: ["my.tools.legend.Window"]

,map: null
,toolbar: null

,constructor: function(config){
this.callParent(arguments);
}

,launch: function(){
// Here I create and lay out the ExtJs components to hold the map and toolbar
// I add some shared tools, like the Legend tool, to the toolbar (hence, the "uses" necessary above)
// I initialize the map to work with the whole world and add some base layers that are displayed for reference in every application (e.g., topology)

this.toolbar = ...
this.toolbar.addTools([legendTool]);

this.map = ...
this.map.addLayers([refLayer1, refLayer2]);
}
}
);


In a /distdir/apps/ExtendedApp*/ExtendedApp*.js, I have the following:


Ext.define(
"ExtendedApp*"
,{
extend: "my.BaseApp"
,name: "ExtendedApp*"
,uses: [
"my.tools.watertraceability.Window"
]
,constructor: function(config){
this.callParent(arguments);
}
,launch: function(){
this.callParent();
:
// add the application-specific layers and tools to the map (e.g., water layer and
// the Water Traceability tool

this.toolbar.addTools([watertraceabilityTool]);
this.map.addLayers([waterLayer]);
}
}
);


Finally, in the "index_*.html" for a given ExtendedApp is the following code:


Ext.Loader.setConfig({
paths: {
"Ext": "/distdir/resources/ext-4.1.1/src"
,"my": "/distdir/src/"
}
});
var app = null;
Ext.onReady(
function(){
app = Ext.create("ExtendedApp1"); // e.g., Ext.create("Water");
}
);


So, to create a new application, I will:
1. Create a new ExtendedApp* directory, with the two index_*.html files and a single javascript file of the same name as the directory.
2. In the index_*.html files, I'll change the 'Ext.create("ExtendedApp1")' line to reflect the name of the new application.
3. Finally, I'll update the new ExtendedApp*.js's launch function to add the application-specific layers and tools.


Hopefully, I've translated what I've actually done properly to the above class names, structure, etc. and I haven't caused more confusion for someone trying to figure out the same type of thing by mis-typing some things.

While, in the end, I may not actually do this by extending the Ext.app.Application as outlined (I'm going to investigate pulling things apart into views and controllers more), this should close out this particular thread which was originally asking how to extend Ext.app.Application as necessary. Next, I'll have to work out how to do a "build" with this structure but I'll likely start a new thread with any questions I have as I try to do that.

Thank you so much to mberrie without whose generous contribution of time and knowledge I could not possibly have worked out all the bits and pieces that were affecting what I was trying to set up. Is there a maximum number of points you can give to someone? And, how can I possibly identify which of the responses is the "correct" answer? I might just mark this one as the answer (although I certainly didn't come up with it on my own...not even close) and hope interested parties will read the rest for a better understanding of the problem and potential solutions.

Cheers,
jtm

mberrie
27 Aug 2012, 8:08 PM
And, how can I possibly identify which of the responses is the "correct" answer? I might just mark this one as the answer (although I certainly didn't come up with it on my own...not even close) and hope interested parties will read the rest for a better understanding of the problem and potential solutions.

That's totally fine with me. It's great that you posted your final solution!