View Full Version : Best way/framework to structure your Ext app.
Cigaro78
03-26-2008, 11:08 AM
Hi, I've been doing web programming for about 12 years now, but I must say the GUI concepts introduced by Ext are new to me.
For example, I struggle to figure out how to organize my GUI code when it gets complex and how to structure my own server side code when the application is so client-side oriented.
Is there a place where I could find more information (I currently use coldfusion, but I'm happy to code in ruby, php or python) - Ideally I'm looking for a web framework designed with Ext in mind.
Thank you for any advice or pointer!
PS: obviously I should have posted this in 'help' and didn't - unfortunately the forums won't let me delete or move this thread. Sorry.
I really like MODx (http://www.modxcms.com/).
Read this post:
MODx (http://modxcms.com/) is indeed incorporating Ext into an upcoming release. It's making it insanely easy to create a very rich application manager.
096 will be our 11th release since founding the project and will be out this week. There's nothing that would restrict you from using Ext with it today, but it won't include Ext by default.
The next release has been in development for more than a year and is a complete rewrite from the ground up into a proper OO web application framework (that also happens to support PHP 4.3.x and above). This upcoming release is built on top of xPDO (http://xpdo.org) and will also use Ext integrated into Smarty for the Manager interface with Mootools 1.0 as the underlying JS lib. It's working great and saving us a ton of time! Mootools plus ExtJS is proving to work well in our case, and we should test 1.1 in the coming week. FWIW, you could also implement the manager in native templates or any other template library by extending the parser.
The Manager is being rewritten using the MODx API ... so MODx is being made from MODx itself. It will have some very nice capabilities including all the things that folks liked about MODx in the first place (incredibly simple XHTML/CSS templates, friendly/SEO URLs, no hierarchy limitations, fast learning curve...) layered in with some more robust capabilities (versioning/rollbacks, publishing work flow, able to extend and alter the core without hacking WHILE maintaining future upgrade path, subsites/multiple sites, and so on).
Lemme know if you'd like more info or just PM me.
crafter
04-04-2008, 03:37 PM
I also came to Ext from a web programming background and was looking for one that I could work with easily with Ext.
However, I have since changed that position and mostly develop Ext based front ends seperately from the server side.
What helps on the server side is I generally use a MVC-based famework that allow me to seperate the server side from the GUI side,
On the front end, I seperate my code into mdoular files that represent my database objects on the server (eg customer.js, product.js ...)
Then within each module I create a singleton funtion so that my "module" is loaded. All my code dealing with that module is embedded within the singleton function.
Then I have one more file, which loads the onReady function which runs when my browser loads. This will contain my main application (fro exmaple the system dashbaord) containing the menus and other links to load the module functions.
Hope this helps.
Collin Miller
04-04-2008, 05:54 PM
I've built up a little extension that I use to clearly separate MVC in my Ext apps. Combined with my IDE code-layout it helps me navigate my application.
Ext.namespace("CollinMiller.Resources");
Ext.Ajax.defaultHeaders = {
'Accept': 'application/json'
};
Ext.QuickTips.init();
CollinMiller.Resources = {
BODY_WIDTH: 760,
name: 'resources',
models: {
resources: ['data.JsonStore', {
root: 'resources'
,id: 'id'
,fields: [
'created_at'
,'updated_at'
,'name'
]
,autoLoad: true
,url: '/resources'
}]
,behaviors: ['data.JsonStore', {
root: 'behaviors'
,id: 'id'
,fields: [
,"type"
,"resource_id"
,"created_at"
,"updated_at"
]
,autoLoad: true
,url: '/behaviors'
}]
},
views: {
resourcesView: ['grid.GridPanel', function() {
return {
store: resources.models.resources
,columns: [
{header: "Name", dataIndex: 'name'}
,{header: "Updated At", dataIndex: 'created_at'}
,{header: "Created At", dataIndex: 'updated_at'}
]
,viewConfig: {
forceFit: true
}
,sm: new Ext.grid.RowSelectionModel({singleSelect:true})
,layout: 'fit'
,tbar: []
,destroyCurrentSelection: function() {
resources.models.resources.remove(this.currentSelection());
console.info('only view delete right there!');
}
,currentSelection: function() {
return this.selModel.getSelected();
}
,title: 'Resources'
,region: 'north'
,height: 200
}
}]
,behaviorsView: ['grid.GridPanel', function() {
return {
store: resources.models.resources
,columns: [
{header: "Type", dataIndex: 'type'}
,{header: "Updated At", dataIndex: 'created_at'}
,{header: "Created At", dataIndex: 'updated_at'}
]
,viewConfig: {
forceFit: true
}
,sm: new Ext.grid.RowSelectionModel({singleSelect:true})
,layout: 'fit'
,tbar: []
// ,destroyCurrentSelection: function() {
// resources.models.resources.remove(this.currentSelection());
// console.info('only view delete right there!');
// }
,currentSelection: function() {
return this.selModel.getSelected();
}
,title: 'Behaviors'
,region: 'center'
}
}]
,newResource: ['Button', {
text: "New Resource"
}]
,editResource: ['Button', {
text: "Edit Resource"
}]
,resourceDialog: ['Window', {
title: "Name your Resource!"
,modal: true
,width: 300
,closeAction: 'hide'
}]
,resourceName: ['form.TextField', {
allowBlank: false
,blankText: 'Name Your Resource!'
,emptyText: 'resource name'
,fieldLabel: 'Resource Name'
,msgTarget: 'qtip'
}]
,resourceForm: ['FormPanel', {
monitorValid: true
}]
,createResource: ['Button', {
text: 'Create Resource'
,formBind: true
}]
,updateResource: ['Button', {
text: 'Update Resource'
,formBind: true
}]
,destroyResource: ['Button', {
text: "Destroy Resource"
,confirm: function(callback, scope) {
Ext.MessageBox.confirm("Really destroy this resource?"
,"Really? This will affect your database and any dependent resources. No undo implemented yet!"
,function(yes) {if(yes) callback.call(this)}
,scope);
}
}]
},
controllers: function() {
var views = resources.views,
models = resources.models;
return ({
viewport: {
render: function() {
this.add(views.resourcesView, views.behaviorsView);
views.resourceDialog.show();
views.resourceDialog.hide();
}
}
,resourcesView: {
render: function() {
this.getTopToolbar().add(
views.newResource,
views.editResource,
views.destroyResource);
}
,'selModel:selectionchange': function(model) {
if (this.hasSelection()) {
views.destroyResource.enable();
views.editResource.enable();
}
else {
views.destroyResource.disable();
views.editResource.disable();
}
}
}
,newResource: {
click: function() {
views.updateResource.hide();
views.createResource.show();
views.resourceDialog.show();
views.resourceForm.getForm().reset();
}
}
,resourceDialog: {
render: function() {
this.add(views.resourceForm);
}
}
,resourceForm: {
beforerender: function() {
this.add(views.resourceName);
this.buttons= [views.createResource, views.updateResource];
}
}
,editResource: {
render: function() {
this.disable();
}
,click: function() {
views.updateResource.show();
views.createResource.hide();
views.resourceDialog.show();
views.resourceForm.getForm().reset();
views.resourceName.setValue(views.resourcesView.currentSelection().data.name);
}
}
,createResource: {
}
,destroyResource: {
render: function() {
this.disable();
}
,click: function() {
this.confirm(views.resourcesView.destroyCurrentSelection, views.resourcesView);
}
}
,resources: {
load: function() {}
}
});
}
};
new Ext.ux.MVC(CollinMiller.Resources);
Ext.ux.MVC:
/**
* @class Ext.ux.MVC
* Experimental Model View Controller class
* @cfg {Object} name The MVC application name
* @cfg {Object} config MVC application configs. Such as base URL.
* @cfg {Object} models The models for the app
* @cfg {Object} views The views for the app
* @cfg {Object} controllers The event handlers for the app
* @constructor
* Create a new Ext MVC app
* @param {Object} config The config object
*
* @author collin.miller@scribestorm.com
*/
Ext.ux.MVC = function(config) {
if (!config.name) {throw "Provide a name for your MVC app. Config options: name:String";}
this.generateSkeleton(config.name);
this.populateSkeleton(config);
this.buildModels();
Ext.onReady(this.bootViewport.createDelegate(this));
}
Ext.ux.MVC.prototype = {
//private
CONSTRUCTOR: 0,
//private
CONFIG: 1,
/**
* Generates an empty application skeleton. Ssets window[name] to the skeleton.
* @param {name} name The name to use when creating the application
*/
generateSkeleton: function(name) {
window[name] = this;
Ext.apply(this, {
models: {},
views: {},
controllers: {},
config: {}
});
},
//private
// fill in the app skeleton with user provided config
populateSkeleton: function(config) {
Ext.apply(this, config);
/* Highly experimental and RoR specific */
// Ext.apply(skeleton.config, {
// resource: {
// site: this.config.site,
// scope: this.app.models,
// transport: Ext.ux.Rest.transport
// }
// });
},
//private
// Iterates over all models.
// If config for a model is a function the result of the function will be used
// as the arguments for the constructor.
buildModels: function(app, resourceConfig) {
var slot, models = this.models, constructor, config;
/* Highly experimental and RoR specific */
// Ext.each(models.resources||[],
// function(resource) {
// Resource(resource, resourceConfig);
// });
for (slot in models) { this.construct(models, slot); }
},
//private
construct: function(scope, name) {
var constructor = this.findConstructor(scope[name]),
config = scope[name][this.CONFIG];
if (typeof constructor === "function") {
if (typeof config === "function") {
scope[name] = new constructor(config.call(this));
}
else {
scope[name] = new constructor(config);
}
}
// else if (constructor === 'function') {
// scope[name] = config.call(this);
// }
},
//private
findConstructor: function(constructor) {
var selector = constructor[this.CONSTRUCTOR];
// if (selector === 'function') {
// return 'function';
// }
// else
if (selector && selector.constructor === String) {
return Ext.delve(Ext, selector);
}
return selector;
},
//private
bootViewport: function() {
new Ext.Viewport({
layout: 'border',
plugins: {
init: this.onViewportBoot.createDelegate(this)
}
});
},
//private
onViewportBoot: function(viewport) {
this.views.viewport = viewport;
this.instantiateViews();
this.applyControllers();
},
//private
// Iterates over all views.
// If config for a views is a function the result of the function will be used
// as the arguments for the constructor.
instantiateViews: function() {
var slot, views = this.views;
for (slot in views) {
if (slot !== 'viewport') {
this.construct(views, slot);
}
}
},
//private
// Looks up named components in the views or models namespaces of the app.
// Uses Obsevable#on to apply function to event.
applyControllers: function() {
var slot, component, componentToUse, event, eventName,
controllers = this.controllers,
models = this.models
views = this.views;
if (controllers.constructor === Function) {controllers = controllers();}
for (slot in controllers) {
component = models[slot] || views[slot] || null;
if (!component) {
console.trace();
throw "Attempted to attach events to non-existant component: "+slot
}
else {
for (event in controllers[slot]) {
splitEvent = event.split(":");
if (splitEvent.length == 1) {
component.on(splitEvent[0], controllers[slot][event]);
}
else {
Ext.delve(component, splitEvent[0]).on(splitEvent.pop(), controllers[slot][event])
}
}
}
}
}
};
Some Support Code:
alert("Quit f***ing with the global namespace!");
Ext.apply(String.prototype, {
unit: function(){
return this.slice(this.length - (this.length - String(parseFloat(this)).length));
},
singularize: function() {
if (this.charAt(this.length - 1) === 's') {
return this.slice(0, this.length -1);
}
return this.toString();
}
});
Ext.delve = function(source, selector) {
Ext.each(selector.split("."),
function(property){
source = source[property];
});
return source;
};
sanjivank
04-06-2008, 08:05 AM
I have simply kept all the fields into fields.js, datastores into datastore.js and so on. I am sure that this is not the best way but it saved considerable rework for me. I like to structure it a lot but time is something that I am short of at the moment:(
deitch
05-01-2008, 03:59 PM
Collin,
This is a mighty interesting piece of work. You should package it up as a separate extension and give it its own post.
The thing that has always bothered me about ext is the too-close coupling in code of the various elements. What you are proposing here is that views get built separately from controllers, and then we attach events to the views connected to the appropriate controllers. You could probably combine this rather nicely with the new application methodology http://extjs.com/learn/Tutorial:Writing_a_Big_Application_in_Ext to do a really clean separation and much shorter config file.
Now, if you add REST to this, which is pretty straightforward, and some kind of inclusion system like JavascriptMVC, you can get a lot of power here.
deitch
05-01-2008, 05:32 PM
I would also be interested in hearing which IDE you are using and how this structure ties in and helps you navigate.
deanna
05-14-2008, 01:15 PM
My take on the framework ideas. MVC is a great paradigm even in Ajax world, but you have to apply it right to get the most benefit. You actually have two applications (making 1 larger application) talking to each other (maybe more than two, but I don't think many of us are doing enterprise apps with jsee or such). The server application should in itself be MVC, and the browser by default is MVC. For the browser the Controller is the browser itself, the View is the DOM, and the Model is all the js code structures. The view output from the server becomes the browsers model. The browser part of the application is for display, user interaction, and event processing. Business logic, access control, second tier data verification, security should all be in the server side.
So for the server side, I use something like Zend Framework (other php frameworks work as well for this). Zend gives the server the MVC framework where your views end up being JSON output, js code (more on that in a sec), or html depending on the action requested. JSON output and html are a bit straightfoward, you just have a view file that outputs that based on the data. The javascript part is the interesting part. You can use something like ExtPHP (http://nexus.zteo.com/2008/03/04/extphp-an-extjs-converterwrapper-for-php-developers/) or PHP-Ext (http://php-ext.quimera-solutions.com/) to generate forms, grids, or whatever UI elements you need to display. This is the equivalent of php generating innerHTML elements, except it generates the extjs code to create those elements inside the extjs paradigm.
Lobos
05-14-2008, 01:49 PM
I really like MODx (http://www.modxcms.com/).
I like soup as well, but not soup made from php...
http://modxcms.com/module-code.html
brian.moeskau
05-14-2008, 02:14 PM
alert("Quit f***ing with the global namespace!");
:-/
deitch
05-14-2008, 03:23 PM
Deanna,
I agree with MVC on the server-side. If you do a good SOUI/SOFEA architecture, then you have all the client-server communication (except for static pages and js pages) coming as JSON or XML. The processing then is on the server-side, and can be anything you like - Java (Spring, JSF, Struts, take your pick for MVC), PHP (Cake, whatever), .Net, whatever excites you.
The challenge I find on the client side. Viewing the browser as the controller and all your js code as the model does not seem completely right. It still opens up to spaghetti code. That is the equivalent of saying that in a classic Web app, the HTTP server is the controller, since it sends the events to your app. If that were true, all these frameworks would be redundant and useless. I still find a need for some better code structuring on the client side.
I did take a look at JavaScriptMVC, which is interesting. I like the include() structure, very clean. The Controller part is weak, though, when dealing with ExtJS. ExtJS uses events within the objects, while JavaScriptMVC assumes all events are browser events, all Observable-equivalents are view objects, and can be bubbled up to the document root and captured that way. In ExtJS, I can have an event on a Store, which is never part of the view.
My take on the framework ideas. MVC is a great paradigm even in Ajax world, but you have to apply it right to get the most benefit. You actually have two applications (making 1 larger application) talking to each other (maybe more than two, but I don't think many of us are doing enterprise apps with jsee or such). The server application should in itself be MVC, and the browser by default is MVC. For the browser the Controller is the browser itself, the View is the DOM, and the Model is all the js code structures. The view output from the server becomes the browsers model. The browser part of the application is for display, user interaction, and event processing. Business logic, access control, second tier data verification, security should all be in the server side.
So for the server side, I use something like Zend Framework (other php frameworks work as well for this). Zend gives the server the MVC framework where your views end up being JSON output, js code (more on that in a sec), or html depending on the action requested. JSON output and html are a bit straightfoward, you just have a view file that outputs that based on the data. The javascript part is the interesting part. You can use something like ExtPHP (http://nexus.zteo.com/2008/03/04/extphp-an-extjs-converterwrapper-for-php-developers/) or PHP-Ext (http://php-ext.quimera-solutions.com/) to generate forms, grids, or whatever UI elements you need to display. This is the equivalent of php generating innerHTML elements, except it generates the extjs code to create those elements inside the extjs paradigm.
deanna
05-14-2008, 04:01 PM
Deitch , I didn't mean to imply the client side should be a miss-mash spaghetti structure. While I don't use a library help to me structure the js code, I usually keep a model of the server side objects that caches data, stores, and has all the remote calls to perform actions on the server. The structures that create and handle all the window and DOM manipulation are separate. But I will take a look at JavaScriptMVC and see if it can add any value to my current techniques - I alway am looking for better ways of doing things.
--------------------------------
After a quick look. I agree with you that the controller functionality is lacking - and ExtJS already has an event system built in that works with all the components. The include functionality seems nice on the surface, but it is compressing at runtime - I had rather minimize my js files and let apache compress them. I don't know if it brings anything to the table other than a different interface between what they call model (what I think of as my remote access objects) and the view (which is the DOM manipulation code, most of ExtJS objects).
deitch
05-14-2008, 04:40 PM
Deanna, I misunderstood you, then.
I did a lot of traditional Web app work before moving to this type of architecture (considering that my day jobs involve running start-ups and management consulting). I am trying to avoid re-learning my old lessons of having a mish-mash and then needing to clean it up later.
Deitch , I didn't mean to imply the client side should be a miss-mash spaghetti structure. While I don't use a library help to me structure the js code, I usually keep a model of the server side objects that caches data, stores, and has all the remote calls to perform actions on the server. The structures that create and handle all the window and DOM manipulation are separate. But I will take a look at JavaScriptMVC and see if it can add any value to my current techniques - I alway am looking for better ways of doing things.
--------------------------------
After a quick look. I agree with you that the controller functionality is lacking - and ExtJS already has an event system built in that works with all the components. The include functionality seems nice on the surface, but it is compressing at runtime - I had rather minimize my js files and let apache compress them. I don't know if it brings anything to the table other than a different interface between what they call model (and I think of as my remote access objects) and the view (which is the DOM manipulation code, most of ExtJS objects).
deanna
05-14-2008, 07:04 PM
As an example, I have a simple class that my server access objects inherit from
GCD.ServerObj = Ext.extend(Ext.util.Observable, {
remoteObj: 'index',
connection: Ext.Ajax,
serverAction: function( action, o){
var rqst = {
url : this.remoteObject + '/' + action,
method: 'GET',
success : this.defaultSuccess,
failure: this.defaultFailure
};
Ext.apply(rqst,o);
return this.connection.request(rqst);
},
defaultSuccess: function(result, request){
Ext.MessageBox.alert('Success', "You forgot to handle success");
},
defaultFailure: function (result, request) {
Ext.MessageBox.alert('Failed', "The remote actions failed");
}
});
This class has the framework to access remote objects. It defaults to the Ext.Ajax connection but can be overridden. Other parts of it are meant to be overridden, such as the remoteObject.
I then override it as in this snippet
GCD.UserClass = Ext.extend(GCD.ServerObj, {
remoteObject: 'user',
config: null,
id: 100,
ready: false,
init: function() {
this.serverAction('get-desktop-config',{
params: {
id : this.id
},
disableCaching : true,
success : function(r, options){
this.config = Ext.util.JSON.decode(r.responseText);
this.ready = true;
}.createDelegate(this)
});
},
getConfig: function(){
return this.config;
},
All data is kept in remote server objects that decide whether it needs a server trip. I also play with caching there to reduce server calls. There are times that display elements are created based on server side data - that is the only case where the display classes contact the server directly (just seems more appro there).
deitch
05-14-2008, 08:47 PM
Interesting. So you are using something like DAO. The Model object on the server side is wrapped by one on the client side, which knows how connect and update.
I like it, but how about event management and controller?
deanna
05-14-2008, 09:11 PM
The event manager is built into ExtJS. I just use it. That is why the base ServerObj inherits from Ext.util.Observable - so I can respond and publish events. It doesn't get in the way that the same event processor is handling the user generated events, in fact that sometimes helps to have both unified. The one thing I don't have a clean solution for is being able to spinlock to wait on data from other multiple objects to arrive. Currently I'll divide the logic where I need the lock and use this pattern
this.restOfLogic.defer(100,this);
},
restOfLogic: function(){
// Spin lock on the configuration returning
if( !Object1.readyFlag || !Object2.readyFlag){
this.restOfLogic.defer(100,this);
return;
}
this.data1 = Object1.getData();
this.data2 = Object2.getData();
// Proceed with processing
With multiple objects you can't just respond to an event. So you have to spin till all are ready.
deanna
05-14-2008, 10:47 PM
Oh, something I missed earlier that you said. This isn't really the same as DAO in that it is wrapping around the server controller objects, not server model objects. The server controller objects take an action and may use any number of model objects, then return data to you through a json view.
deitch
05-15-2008, 08:03 AM
Yes, I see that. From the client's perspective, though, they appear as model objects, no?
deitch
05-15-2008, 08:08 AM
Not sure I get this. Within ExtJS, each object that inherits from Observable can publish events. Thus, I might have user events - clicking on a row in a grid, for example - or non-user events - change in data or store load or loadexception, for example. The "traditional" ExtJS-way appears to be to take whatever code you want (or object, etc.) and just register it as a handler on the particular instance of Observable-inherited object. Each of these becomes, to some extent, model and controller. It then may update the view. This works well for small apps, but does not scale fabulously well for more complex apps and is hard to unravel, which is why MVC is so useful in the first place. The FrontController pattern (which is not always followed) isolates one point for all events, and then passes them to the appropriate specific controller, is supposed to alleviate some of this.
How do you cleanly isolate the various event-handlers (a.k.a. controllers) from the views (a.k.a. JS-code that generates the UI) and the rest?
I am sure I am missing something you are saying. Sample code, perhaps?
Thanks.
The event manager is built into ExtJS. I just use it. That is why the base ServerObj inherits from Ext.util.Observable - so I can respond and publish events. It doesn't get in the way that the same event processor is handling the user generated events, in fact that sometimes helps to have both unified.
deanna
05-15-2008, 08:09 AM
Yes and no, depending on which perspective you are looking through at the moment. Yes, it is the clients model of the server (as the controller is THE interface to the server part of the app), and No when you look at it from what part of the server it wraps. Orthogonal views of the same picture. It was late last night when I posted and was thinking in the later view, but both views are simultaneously correct.
deitch
05-15-2008, 08:11 AM
Makes sense. The architecture clearly distinguishes between client-side and server-side. The server-side I am very clear on, done lots of. The communications channel is also very clear: a well-defined protocol, using XML or JSON or whatever makes you happy.
The part I am still trying to wrap my head around is structuring the client app, beyond the simple "we can just write some ExtJS code" but rather providing real structure.
Yes and no, depending on which perspective you are looking through at the moment. Yes, it is the clients model of the server (as the controller is THE interface to the server part of the app), and No when you look at it from what part of the server it wraps. Orthogonal views of the same picture. It was late last night when I posted and was thinking in the later view, but both views are simultaneously correct.
deanna
05-15-2008, 08:57 AM
Lets start by saying, I don't think I have the best, or absolutely the correct way of structuring or designing applications. I'm always looking for better ways. This is my current mental model.
As I pointed out in an earlier post, I think of the browser as part of the MVC with the DOM as the View and the Controller the browser's event handler - which extjs extends. I don't think the js code fits the mvc pattern in and of itself, but is more akin to the Model in the MVC pattern. Although it does have an extension of the browsers Controller (as the ExtJS event processor), and it does interact with the DOM. The DOM has to be considered our View as that is our output. Our View (the DOM), then becomes the browsers model to turn into the users view the pixels on the screen.
This is the way I use it in my code with no need for a secondary controller inside what is the model. The Model is the JS structures, which is composed of 3 basic parts (not MVC), the remote access objects, the parts that construct the view/DOM (view generating objects) and behaviors (things that respond to events). Behaviors are not separate objects, but just methods of the view generating objects. To be cleaner I could break them out, but they usually have knowledge and need a specific view generating object.
As for keeping things clean, this is the basic design.
The view generating objects are responsible for creating the DOM and handling user generated events with behaviors. In response to those events, they may need data which they request from the remote server objects. The remote server objects fire an event when they have done their task that the view generating objects respond to and handle the data. The same applies to the remote server objects saving data.
I try to keep the remote server objects as thin as possible, with little behaviors as possible (only tier 1 data checking and such). The only exception to this would be (and I haven't had to do this yet) is if the remote object was aggregating data from some other source I didn't have control over. The remote server objects NEVER respond directly to user events. They may respond to other remote server events that indicate the data has returned in order for them to complete their task.
I don't try to code anything other than the UI as part of the client. If the client starts doing business logic or making decisions, then your system becomes open to hackers. I verify on each server action at the server if the user has access to that action.
Does this help you see how I'm thinking about the structure. The browser event handler initiates actions, which are processed through the ExtJS event handler and sent to the view generating object which responds. The view generating objects (which are actually js models of what will be output as the DOM) has coded behaviors that responds. If the view generating object needs to request or store data, it access the remote server objects, where it waits on an event from the server object to say it has completed.
What can you suggest to make it cleaner? How are you envisioning the client?
deitch
05-15-2008, 09:54 AM
Lets start by saying, I don't think I have the best, or absolutely the correct way of structuring or designing applications. I'm always looking for better ways. This is my current mental model.
Likewise. Anyone who is convinced they have the best or absolutely correct model by definition probably has an awful one.
As I pointed out in an earlier post, I think of the browser as part of the MVC with the DOM as the View and the Controller the browser's event handler - which extjs extends. I don't think the js code fits the mvc pattern in and of itself, but is more akin to the Model in the MVC pattern. Although it does have an extension of the browsers Controller (as the ExtJS event processor), and it does interact with the DOM. The DOM has to be considered our View as that is our output. Our View (the DOM), then becomes the browsers model to turn into the users view the pixels on the screen.
Ah, here is where I tend to disagree. I think that what we do in js code using ExtJS is so complex as to warrant proper MVC separation. Obviously, some simple UI enhancements using ExtJS components need not worry about it. But, if you really build a complex single-page application, with lots of communication with the server (e.g. over json), then you have a lot going on. With all the different events, I would want to see a clearly defined "in response to any event, a well-defined component called a controller is called, that processes the event, calls the appropriate business logic, modifies/retrieves the model, and calls view changes." I also tend to view the browser and DOM not as the view but the container for the view, the same way that the browser and DOM are not the view but the container for the view in traditional Web apps or an MS Window is for desktop Windows apps. But perhaps I am misunderstanding your reference to the DOM?
This is the way I use it in my code with no need for a secondary controller inside what is the model. The Model is the JS structures, which is composed of 3 basic parts (not MVC), the remote access objects, the parts that construct the view/DOM (view generating objects) and behaviors (things that respond to events). Behaviors are not separate objects, but just methods of the view generating objects. To be cleaner I could break them out, but they usually have knowledge and need a specific view generating object.
This sounds a lot like MVC, since the remote access objects are kind of like models, the view-generating objects are like the view (they contain all that is needed to get a view), and the behaviours are like controllers. You wouldn't want to share some sample code that you have done, would you? (hint, hint)
I try to keep the remote server objects as thin as possible, with little behaviors as possible (only tier 1 data checking and such). The only exception to this would be (and I haven't had to do this yet) is if the remote object was aggregating data from some other source I didn't have control over. The remote server objects NEVER respond directly to user events. They may respond to other remote server events that indicate the data has returned in order for them to complete their task.
Agree wholeheartedly. This is the heart of SOUI/SOFEA design, and I see as a very strong coming trend on the Internet. Actually, this is precisely why I developed a javascript-native internationalization library, because, in the end, the user interface part should not be done on the server, and all existing i18n (Java, PHP, etc. etc.) are all server-based.
[quote=deanna;168527]I don't try to code anything other than the UI as part of the client. If the client starts doing business logic or making decisions, then your system becomes open to hackers. I verify on each server action at the server if the user has access to that action. [/code]
Hmm, not sure I agree here. I have no problem with business logic on the client, although I could see it both ways, as long as validation happens on the server. In the end, if I allow the client to submit a request to add a new user to a bulletin board, for example, then that is already business logic.
This is a great conversation. I would really like to see some formalized MVC framework for ExtJS (or other JavaScript), without some of the JavaScriptMVC limitations you pointed out. I am thinking of some ideas...
deanna
05-15-2008, 01:00 PM
I don't have any code that I can share without writing some new stuff just to demonstrate (we are keeping the source private since there are other companies that are in the same business - and yes I will be buying a developer license before/if we launch the project :) )
So if you think the remote object/view generating object/ and behavior object sounds like a workable pattern, maybe we could figure out a re-usable framework that supports that. Is having the behaviors part of the view generating objects clean enough. I will do something like this
newClass = Ext.extend(someclass, {
// config stuff
// initComponent and onRender
// support methods for initComponent and onRender if needed
// behavior methods
}
deitch
05-15-2008, 01:03 PM
So if you think the remote object/view generating object/ and behavior object sounds like a workable pattern, maybe we could figure out a re-usable framework that supports that. Is having the behaviors part of the view generating objects clean enough. I will do something like this
newClass = Ext.extend(someclass, {
// config stuff
// initComponent and onRender
// support methods for initComponent and onRender if needed
// behavior methods
}
Not sure I get the last part, but yes to the former.
Let's think about it from the beginning. The key ideas behind MVC are to have clean separation of concerns, so that all events go through easily-determined controllers; all view changes go through easily-determined views; and all data go through easily-determined models. Anything that clearly does this hits the nail on the head.
deanna
05-15-2008, 01:12 PM
The pattern was showing that the behaviors are part of the view class, but separated from the other code just by coding convention. Actually I guess I could seperate the 'view only' part from the behavior and attach the behavior by extend-ing the view class with the behaviors but I would need a standard interface to set up the handlers - like a behaviorInit method which would attach listeners and actions to view elements. behaviorInit would have to be checked for existence and called in initComponent. This still requires a behavior to be very aware of the view's parts though - but it could separate them as a design and even file structure.
deitch
05-15-2008, 01:15 PM
Yes, there was a discussion about MVC in another thread, but all of them required some level of saying, "OK, register all this with the MVC framework."
I had thought Junction was interesting at one point, but it really does seem obsessed with having the RDBMS local or on the server, and cannot detach the model from the RDBMS enough to support just talking some arbitrary protocol. It is also difficult to connect with ExtJS.
deanna
05-16-2008, 08:36 AM
Based on our discussion, I think I'm going to go back and add a central event controller for events between the remote server objects (model) and the behaviors. It doesn't help decouple the behaviors from the view generating objects, but it does create a central subscriber/publisher place for all app generated events.
deitch
05-16-2008, 08:56 AM
Cool. Please do share what you do, I would love to see it, perhaps build on it.
I have been thinking a lot about the event flow model in ExtJS. The more I think about it, the more it seems to resemble Java Swing. Swing has components as well, with built-in events and handlers, so that, in theory, the model and controller are one component. See http://java.sun.com/products/jfc/tsc/articles/architecture/
Based on our discussion, I think I'm going to go back and add a central event controller for events between the remote server objects (model) and the behaviors. It doesn't help decouple the behaviors from the view generating objects, but it does create a central subscriber/publisher place for all app generated events.
deanna
05-16-2008, 09:35 AM
The coding won't be much other than a list of events Observable handles most of the processing - although I am overriding the suspendEvents with Animals queue on suspend logic. It isn't very elaborate, just a central place to handle events.
The basic code is this
GCD.Classes.EventController = Ext.extend(Ext.util.Observable, {
constructor: function(){
GCD.Classes.EventController.superclass.constructor.apply(this, arguments);
this.addEvents(
{"event1": true},
{"event2": true},
{"eventXXX": true}
);
}
});
GCD.EventController = new GCD.Classes.EventController();
Then in the view I subscribe to the event
GCD.EventController.on( {'eventXXX' : {
fn: this.buildXXX,
scope: this,
single: true
}});
GCD.ModelObject.requestXXX();
Then in requestXXX
requestXXX: function() {
this.serverAction('xxx',{
params: {
id : this.id
},
disableCaching : false,
success : function(r, options){
this.xxxdata = Ext.util.JSON.decode(r.responseText);
GCD.EventController.fireEvent('eventXXX', this.xxxdata);
}.createDelegate(this)
});
},
vBulletin® v3.8.4, Copyright ©2000-2010, Jelsoft Enterprises Ltd.