View Full Version : From HTML Markup or dynamically from script?
Wolfgang
2 Jan 2008, 6:29 AM
I wonder what is best practise when generating HTML container and the like that should house EXT Objects.
For example if i want to create a panel i can:
(...this applies to any component, not just panels):
a.) Create the panel entirely dynamically from script, load content via XHR.
b.) Create the necessary DIV container in HTML, render the Panel into it, load content via XHR.
c.) Create the necessary DIV container in HTML, create the Panel content in HTML, render the Panel into it
combine b+c) Create the necessary DIV container in HTML, create the Panel content in HTML, render the Panel into it, load further panel updates via XHR.
On the first glance i like a.) pretty much. The benefit: There is no need to define HTML ID's accross the Javascript code and the HTML Markup creation code.
However, b.) gives a better separation of code and design.
c.) Is much like b.) but seems to work more with static content inside the panel.
So combining b+c) seems to be a good choice too, since it allows the creation of a skeleton that can be dynamically handled. But it suffers from the need to snyc the HTML ID's just as b.) and c.)
So what do you think?
mw-flow
7 Jan 2008, 1:14 AM
Hi *,
the question Wolfgang asked seems very fundamental to me. It is exactly the question I wanted to ask all you experienced Ext users/developers. In my company, we're a small team and are just about to begin defining what needs to be done to port our application UI to a more contemporary technology. To us, Ext is one of the favourite frameworks to choose from, but we're unsure about such important questions like the one asked above. We'd be very glad if some folks could post their experiences, ideas for best practice, and so on.
Thanks a lot!
mdissel
7 Jan 2008, 4:23 AM
I think you should either choose a or c.. If you choose a combination you got dependencies between the server-side "view" and the client "view"...
For the moment i choose a), but still one thing to test is performance.. Anyone did this test already? (for complex forms / panels).
Thanks
Marco
heidtmare
7 Jan 2008, 7:27 AM
For panels to be present on page load i use c.) but for new panels added to the page after load i use a.)
I prefer to use a.) all the time, but it doesn't make since to do multiple requests for data on the initial page load.
I also sometimes use a store to save panel content and i have code that creates new panels whenever my store gets a new item, and updates existing panels when the store record gets updated.
Rocco
7 Jan 2008, 11:38 AM
Let's say a component's content is dependent upon the user's security role or something (i.e an admin user has more toolbar menu items than a regular user, etc). I had planned to go about this by defining the "core" toolbar and retrieving its config from the server (the server would generate config/items appropriate to the security role). So, something like this:
Ext.onReady(function(){
//reportItems variable populated dynamically via XHR
var reportItems = [{text: 'Report 1'}, {text: 'Report 2'}, {text: 'Report 3'}];
var menu = new Ext.menu.Menu({
id: 'mainMenu',
items: reportItems
});
var tb = new Ext.Toolbar();
tb.render('toolbar');
tb.add({
text:'Reports',
menu: menu
});
});
As I understand it, this is essentially what Wolfgang descibes as option B.
In this example, the server would be returning a generic items array...I suppose it would be better to have it return an actual Ext object, a Menu object in this case? Or go one step further and have it return a toolbar object? I've seen several posts where server-side wrappers have been written for Ext components, but I don't know if I need/want to go that far.
I know that the content of the app's components need to be dynamic...I'm just trying to figure out the best way to go about this. Any thoughts on this approach?
mw-flow
10 Jan 2008, 2:57 AM
When I think about all your good suggestions and all your examples from a more general point of view, I get the impression that whether it is better choosing Wolfgang's a) or choosing c) depends on what kind of application you're implementing.
Two statements on this: :-?
In an application where many different "Presentation Flows" occur (i.e. many different kinds of screens are presented to the user, user navigates quite often from one screen to another) it might be better to use Wolfgang's a), i.e. build all contents on-the-fly using JavaScript.
In an application where similar data are presented to the user on a screen the layout of which changes only rarely, it might be better
to use b) or even c).
I'd be glad to read your comments, ideas, and thoughts on the above two statements.
Wolfgang
16 Jan 2008, 4:13 AM
Let's say a component's content is dependent upon the user's security role or something (i.e an admin user has more toolbar menu items than a regular user, etc). I had planned to go about this by defining the "core" toolbar and retrieving its config from the server (the server would generate config/items appropriate to the security role). So, something like this:
...
As I understand it, this is essentially what Wolfgang descibes as option B.
...
Basically that is not what i meant with b.) ;) because in your example you load JSON or XML (so just data) from your backend to update the toolbar - there is no markup involved.
Nevertheless this is another important aspect of the design question i raised and should be something like option d.) where the object is created in JS, but its configuration is pulled via XHR.
...
Two statements on this: :-?
In an application where many different "Presentation Flows" occur (i.e. many different kinds of screens are presented to the user, user navigates quite often from one screen to another) it might be better to use Wolfgang's a), i.e. build all contents on-the-fly using JavaScript.
In an application where similar data are presented to the user on a screen the layout of which changes only rarely, it might be better
to use b) or even c).
...
I do not think that the decision wether to use a.) or c.) should depend on the _number_ of different "Presentation Flows", but more on how the data is provided and used.
Example:
I want to create a simple panel that shows a number of menu items as links:
<ul id='m-testpad-panel-update'>
<li><a id="m-testpad-panel-add-welcome" href="#">panel-add-welcome</a></li>
<li><a id="m-testpad-panel-remove-welcome" href="#">panel-remove-welcome</a></li>
</ul>
and i want to attach some handler:
Ext.get('m-testpad-panel-add-welcome').on('click', function(a,b,c){
// do stuff here
});
Ext.get('m-testpad-panel-remove-welcome').on('click', function(a,b,c){
// do stuff here
});
To attach the handler, the markup elements must exist. (otherwise Ext.get(would fail)
So i cannot simply use a.), bc the markup would exist only after the asynchrones XHR call.
( or i would need to setup an on load handler...)
Using c.) would work, bc the markup is still there.
However, if i want to update the panel content later on via XHR (to avoid a page reload), what happens to the eventhandler?
It seems that the (new) option d.) where objects are created in the JS world that pull configuration items is an interessing way to go, too.
Going this route and speaking of a type of MVC design pattern, the backend would then really only become the Model that provides data to the View (which would be an Ext based layout with Ext components).
The Ext eventhandler would act as controllers passing their request to the backend controllers which would in turn return data through the backend Model. (Similar to a HMVC or even PAC approach). So there would be no longer a need for generating views (HTML markup) via the backend.
violinista
17 Jan 2008, 7:56 AM
Interesting question.
I consider Ext primarily as View/Controller layer of application, and involve minimal HTML into my web application: i have only one HTML file in my just finished web app which have >10 modules, and each .JS file containing each module. Main module (called frmMain.js :) ) contains main BorderLayout and simple loader which I have built recently. Here is simplified example:
myApp.userModules={
moduleName:'Expenses',
moduleName:'Users',
moduleName:'Invoices'
}
/**********************
* Module Loader::
***********************/
MyApp.userModules.forEach(
function(el,counter){
layout.add('center',
new Ext.ContentPanel(Ext.id(), {
autoCreate:true,
title: el.moduleName,
closable: false,
background:true,
//Example:: id:'frmExpenses'
id: "frm"+el.moduleName,
fitToFrame:true,
url:{
/* Example Pattern::
* 'view/frmExpenses.js' ;)
*/
url: "view/frm"+el.moduleName+".js",
scripts: true,
nocache: true
},
loadOnce:true
})
);
}
);
So, mine answer is totally a) :) I don't like to mess HTML and .js code, and I'm enjoy all of Ext's features, from creating HTML to OOP-fashion of coding with module pattern.
Cheers!
Wolfgang
29 Jan 2008, 3:54 AM
Nice loader.
So far i came also to the conclusion to let Ext be the Controller/View part.
However, i load all the js modules in the main page.
To solve the issue getting php vars in the Js world (for example language translations for Titel, Header etc.), i get all module variables and language strings from the php world via an XHR call that populates an application wide JS data object.
So i ended up with a two stage init process:
1st stage:
- create a sort of semaphore (Ext.ux extension) that will be released later on (requires two release requests)
- create inline javascript that populates the application wide JS data object via XHR
- create a list for all modules and their js files and bild a main page in the php world
- load that main page
In the onReady event of the loader, i release the semaphore one time.
In the success handler that populates the global data object, i release the semaphore a second time.
Then the semaphore triggers the mainpage. (as if the mainpage would have invoked the onReadyEvent). This way i can be sure that all necessary data is available.
2nd stage:
Now the mainpage starts initilizing the modules. Also this is a two step process, because it could be, that modules depend on other modules to work.
So each module must provide an init and setup method (Where the setup is only rarely needed).
Here some example code, it is still in a design phase
The loader: Initializes the semaphore and populates the global data object.
// Start the loader
App.loader = new Ext.ux.Semaphore();
// make sure any subscriber has to wait for onReady
App.loader.acquire();
// make sure any subscriber has to wait for the following AJAX request to return succuessful
App.loader.acquire();
Ext.Ajax.request({
url : App.SiteUrl + '/main/get_params',
params : { action : 'get_init_param' },
waitMsg: 'Initializing, please wait...',
method: 'POST',
success: function(result, request) {
try {
AppData.Modules = Ext.util.JSON.decode(result.responseText);
}
catch(err) {
console.error('App.loader - Get_params: JSON decode Error');
alert('Loader: JSON Server Error!\n\n' + result.responseText);
return;
}
// release only on success
App.loader.release();
console.info('App.loader - Get_params: OK');
},
failure: function(result, request) {
var f = Ext.util.Format;
console.error('App.loader - Get_params: Server Error');
alert('Loader: Server Error!');
//Ext.MessageBox.alert('Server Failed', f.stripTags(result.responseText));
}
});
Ext.onReady(App.loader.release, App.loader);
console.info('App.loader: Done');
The main application script: Executes when the semaphore is finally released. Builds the applicationwide viewport, calls init and setup for each module and attaches any panel of a module to a "section".
NOTE: i removed the viewport part to make code better readable.
App.main = function(){
Ext.QuickTips.init();
Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
// Allow any Contentpanel to provide its own javascript and execute it
//Ext.UpdateManager.defaults.loadScripts = true;
// BUILD VIEWPORT HERE
// THE VIEWPORT CONTAINS ITEMS THAT CAN BE REFERENCED AS SECTIONS
// THE MODULES CAN ATTACH TO THESE SECTIONS
// apps section objects indexed by sectionName. Section is any container like a panel or tabpanel inside a region
// define the sections that can be referenced by any module
App.sections = {};
// section can be any name like "mainmenu" or "central-overview. For testing use the corresponding regionnames
App.sections['south'] = Ext.getCmp('south-panels');
App.sections['south'] = Ext.getCmp('south-panels');
App.sections['east'] = Ext.getCmp('east-panels');
App.sections['center'] = Ext.getCmp('center');
App.sections['west'] = Ext.getCmp('west');
console.info('App.main: Done');
App.Utils.ModuleMgr.initModules();
console.info('App.main - initModules: Done');
App.Utils.ModuleMgr.setupModules();
console.info('App.main - setupModules: Done');
// Setup all panels for the given module
App.Utils.ModuleMgr.showModulePanels(App.Modules.currentuser);
App.Utils.ModuleMgr.showModulePanels(App.Modules.welcome);
};
App.loader.on('unlock', App.main, App, {single: true});
Here a simple module:
/* Note: each module can return multiple panels!
* Panel config should contain:
* id:'m-welcome-testpanel', // id of this component
* sectionName: 'center', // section to use in main layout
* activate: true, // should this panel be shown (if the module returns more than one panel for a section)
* closable: true, // can the user close this panel? (NOTE: this requires actually to add the panel in a tabpanel everytime)
* closeAction: 'hide', // when the user close, only hide the panel - do not destroy it!
*
*/
App.Modules.welcome = function(){
var welcomePanel, testPanel;
return {
name:'Welcome',
/**********************************************************************/
init: function(){
console.info("Welcome.init: Done");
},
/**********************************************************************/
setup: function() {
// must be defined. Will be called from App.main after init()
console.info("Welcome.setup: Done");
},
/**********************************************************************/
getPanels: function(panelId){
// welcome panel
if (!welcomePanel){
welcomePanel = new Ext.Panel({
id: 'm-welcome-panel', // id of this component
contentEl: 'm-welcome', // id of existing element to use as body
title: 'Welcome Box',
sectionName: 'west',
activate: true,
closable: true,
closeAction: 'hide',
autoScroll: false
});
}
if (panelId && welcomePanel.id == panelId) {
return [welcomePanel];
}
// testpanel panel
if (!testPanel){
testPanel = new Ext.Panel({
id: 'm-welcome-testpanel', // id of this component
html: '<h1>welcome testpanel</h1><p> bla vla </p>',
sectionName: 'center',
activate: true,
title: 'Welcome Box Test',
closable: true,
closeAction: 'hide',
autoScroll: false
});
}
if (panelId && testPanel.id == panelId) {
return [testPanel];
}
// return all panels
if (panelId) {
console.error('Welcome: Error requested unknown panel:%s', panelId);
return false;
}
return [welcomePanel, testPanel];
} // getPanels
} // return
}();
I still need to clean up some areas of the code to move all stuff to a.) and get rid of any HTML. Also i am looking to get the configuration itself via the backend (php world). That is where the global data object comes into play that is populated, before the modules get initialized.
Puh what a long posting....;)
Powered by vBulletin® Version 4.1.5 Copyright © 2012 vBulletin Solutions, Inc. All rights reserved.