PDA

View Full Version : Dynamically adding controllers and views



nvoxland
4 May 2011, 1:19 PM
I've been looking at the changes with Ext 4 and trying to figure out how to fit the new MVC framework into our architecture.

We have an setup with a standard tab/subtab navigation, but the goal is to have the tabs and subtabs be built via a plug-in system and have the javascript for each sub-tab be lazy-loaded when they are first accessed rather than pulling them all down at initial render time.

I really like what I see reading through the MVC documentation, but do all views and controllers need to be registered with the Ext.Application call? Or is there a way for the initial Ext.application can be created with no controllers defined, and have new controllers registered as each sub-tab is clicked for the first time?

Nathan

dnorman
13 May 2011, 9:59 PM
/\ THIS /\

Having bought the license for Ext4 and Designer, I must say I'm more than a little frustrated about this, and other similar issues.

I've been pouring over all the available documentation, and at best the documentation (http://dev.sencha.com/deploy/ext-4.0.0/docs/guide/application_architecture.html) is abysmal.

Overall, I really like many of the design/architecture decisions that went into Ext4, which seem pretty well thought out.

But, unless I'm failing to grasp some key factor, it would seem that the new MVC pattern is pretty ill-conceived.

The old MVC pattern used for Ext Designer where the controller was a subclass of the view ( while far from perfect ) is much better, insofar as it actually works with a large application.

It would make far more sense to just have the controller declare it's default view, then create the view vicariously upon instantiation of the controller object, or vice versa.

I can find zero usable examples of an MVC application on the sencha.com sample page that might clue me into whether I'm missing something. The two that are up are compressed using the sencha builder, thus totally unreadable... so I'm not quite sure why they're even on the sample page.

What really irked me more than anything is that for some crazy reason, the application object instantiates the controllers immediately? On what planet does that make sense? My application will easily have 50-100 controllers, which would be far more sensibly instantiated on the fly, when the user clicks the menu item that opens that part of the application.

Lastly, the controller refs thing makes no sense. Why should a controller have to hook into the view and make a bunch of awkward accessor methods with complex selectors? It would make much more sense if the view declared it's touchpoints ( as in Designer with autoref ) and then you accessed them through a properly, like controller.view.foothingus

Given how undercooked the 4.0 release clearly is and the notable lack of devs on these boards, I'm sorry to say that I'm really beginning to question my purchase. Saki and the other members of the community support team are doing an excellent job, and their efforts are much appreciated... but it's not a substitute for some explanation here.

I'm pretty much ready to scrap my migration to Ext4 and just keep going on 3.3.1
Maybe in 6 months 4.x will actually be ready for prime time.

zodeus
17 May 2011, 1:37 PM
@dnorman
I Agree with everything you say here. Two of our developers have spent days now scouring source code trying to make heads or tails of things. Point is seems MVC is only a thought at this point, and is missing a lot of the gluey pieces. :(.

Except for maybe this :), we will continue forward, and we have hundreds of controllers.


/\ THIS /\

Given how undercooked the 4.0 release clearly is and the notable lack of devs on these boards, I'm sorry to say that I'm really beginning to question my purchase. Saki and the other members of the community support team are doing an excellent job, and their efforts are much appreciated... but it's not a substitute for some explanation here.

I'm pretty much ready to scrap my migration to Ext4 and just keep going on 3.3.1
Maybe in 6 months 4.x will actually be ready for prime time.

aclarkd
18 May 2011, 8:02 AM
We basically have the following right now.

the following loads the screen and home controller, the only ones loaded by default. The code in the launch function is for deep linking.



Ext.application({
name: 'AppName',
appFolder: '/js/megaman/src',
controllers: [
'system.Screen',
'Home'
],
launch: function() {
var token = Ext.History.getToken();
if(token) {
this.getController('system.Screen').renderScreen(token);
}
}
});


The renderScreen function in the Screen controller is where the dynamic loading comes in. The screen controller has the following.



init: function() {
Ext.History.on('change', this.renderScreen, this);


This monitors the history and calls renderScreen. renderScreen loads the controller for the token passed to history, based on a mapping.



renderScreen: function(token) {
/* SETUP */
var parts = token.split('.');
var component = parts[0];

/* SCREEN MENU / Context setting*/
if(this.activeComponent != component) {
console.log('changing menu:', this.activeComponent + ' != ' + component);
this.activeComponent = component;
var menu = this.getScreenMenu();
menu.removeAll();
menu.add(Ext.create('AppName.view.system.menu.' + component));
}

var text = '';
switch(component) {
case 'ClientAdmin':
text = this.activeClient.name;
break;
}
this.setComponentContext(text);

/* SCREEN */
var views = [{
token: 'Home'
},{
token: 'ClientAdmin.Home'
},{
token: 'ClientAdmin.ReportSettings',
controller: 'AppName.controller.clientadmin.ReportSettings'
}];
views.forEach(function(i){
if(i.token== token) {
if(i.controller){
var controller = this.getController(i.controller);
controller.init(this);
}
}
}, this);
}


this is the controller that is loaded by renderScreen, it loads the "default view" which is a tab panel. I then add the other views to it. They are grids that are used for user settings.

And yes the refs part is less than ideal.



Ext.define('AppName.controller.clientadmin.ReportSettings', {
extend: 'Ext.app.Controller',
views: ['grid.ReportGroup','clientadmin.ReportSettings'],
stores: ['ReportGroup','Project'],
models: ['ReportGroup','Project'],
refs: [
{ref: 'componentContainer', selector: 'componentcontainer'}
],
init: function() {
var view = Ext.create('AppName.view.clientadmin.ReportSettings');
var tab = Ext.create('AppName.view.grid.ReportGroup');
view.add(tab);

var c = this.getComponentContainer();
c.removeAll();
c.add(view);

var projectStore = this.getProjectStore();
}
});


default view



Ext.define('AppName.view.clientadmin.ReportSettings', {
extend: 'Ext.tab.Panel',
title: 'Report Settings',
items: [
{
title: 'Tab 1',
bodyPadding: 10,
html : 'A simple tab'
}
],

initComponent: function() {
this.callParent(arguments);
}
});


tab two view



Ext.define('AppName.view.grid.ReportGroup' ,{
extend: 'Ext.grid.Panel',
title: 'Report Groups',
store: 'ReportGroup',
storeId: 'reportGroupStoreId',
columns: [
{header: 'Group', sortable:true, dataIndex: 'name', id: 'name'},
{header: 'Projects', sortable:true, dataIndex: 'projectNames'}
],
initComponent: function() {
this.callParent(arguments);
}
});


Store for tab 2, full path to model is required even though it is specified under model. We are using direct but have unsolved issues with the direct store at the moment so we are making it manually. We believe them to be an issue with load order.



Ext.define('AppName.store.ReportGroup', {
extend: 'Ext.data.Store',
storeId: 'report-group-store',
//autoDestroy: true,
//autoSave: true,
autoLoad: true,
batch: true,
model: 'AppName.model.ReportGroup',
proxy: {
type: 'direct',
api : {
read: AppName.Direct.ReportSettings.readGroups,
create: AppName.Direct.ReportSettings.saveGroup,
update: AppName.Direct.ReportSettings.saveGroup,
destroy: AppName.Direct.ReportSettings.deleteGroup
}
},

sorters: {
property : 'name',
direction: 'ASC'
}
});


model for the store.



Ext.define('AppName.model.ReportGroup', {
extend: 'Ext.data.Model',
idProperty: 'groupId',
fields: [
{name: 'groupId', type: 'int'},
{name: 'name', type: 'string'},
{name: 'projectIds'},
{name: 'projectNames'}
]
});


the following lives in our viewport and is where our views are rendered.



Ext.define('system.ComponentContainer', {
extend: 'Ext.container.Container',
alias: 'widget.componentcontainer'
});




{
xtype: 'componentcontainer',
region: 'center',
layout: 'fit',
margins: '5 5 5 5',
items: {xtype: 'homescreen'}
}

aclarkd
27 May 2011, 9:29 AM
Just some things you should be aware of if following this.

You will need to attach the view to the controller if your going to use it in a function called by control.

You need to pay attention to the load order.

my controller looks like this currently... work on progress.



init: function() {
var me = this;
me.control({
'reportGroupGrid': {
itemdblclick: me.editReportGroup
}
});

me.mainView = null;

var reportGroupStore = me.getClientReportGroupStore().load({
callback: function(records, operation, success) {
me.mainView = Ext.create('AppName.view.clientadmin.ReportSettings');
me.getController('system.Screen').renderScreen(me.mainView);
}
});

me.getClientReportVersionOverrideStore().load();
me.getProjectStore().load();
},

editReportGroup: function(grid, record) {
this.mainView.down('reportGroupForm').loadRecord(record);
}