PDA

View Full Version : Inherited controller issue



jmozley
10 Apr 2012, 5:31 AM
I'm using the MVC framework and wanted to have all form views extend a base form view and all form controllers extend a base form controller. The idea being that the base controller will provide standard handlers for submission, dealing with errors, etc.

What I am having difficulty with is the base controller (at least that is where I believe the problem to be). When I have a number of forms in a tab panel I see submission times N, where N is the number of forms open. The URL is the correct one for the form, it is just posted to multiple times.



Ext.define('MyApp.controller.BaseFormController', {
extend: 'Ext.app.Controller',

init: function(application) {

this.control({
'#submit' : { click : this.submitForm },
'#close' : { click : this.closeForm }
});

},

/**
* Called after Submit button is clicked
*/
submitForm : function (button, event) {
var parentFormPanel = button.findParentByType('form');

parentFormPanel.submit({
url : parentFormPanel.url,
scope : this,
success : this.onSubmitSuccess,
failure : this.onSubmitFailure,
params : {cmd:'save'},
waitMsg : 'Saving...'
});
}, // eo function submitForm


Any advice on what I am doing wrong?

börn
10 Apr 2012, 7:01 AM
did you debug this with a breakpoint in the submitForm function? I don't see anything wrong with that - with the #submit query I would think that you only get one listener configured - because an id only exists once in the dom...

if this isn't the case, then the tabs/cards are configured in a wrong way... but this doesn't smell like a inheritance problem?

jmozley
10 Apr 2012, 11:59 PM
I can see that the ID of the button is the same in each submission if I log the arguments of the handler to the console as below:



submitForm : function (button, event) {
console.log('submitForm',arguments);

var parentFormPanel = button.findParentByType('form');


I suspect my problem is in the init function of the base form controller (or rather my understanding of this). There is no application object passed to this:



init: function(application) {

console.log('initBaseFormController', arguments);

this.control({
'#submit' : { click : this.submitForm },
'#close' : { click : this.closeForm }
});

},


There seem to be no arguments passed to the init function of the base form controller from the extended form controller. I can see that the controller I create by extending the base controller is passed the application object:



init : function ( application ) {

console.log('initExtendedFormController', application);

this.callParent(application);
},

vietits
11 Apr 2012, 1:27 AM
There seem to be no arguments passed to the init function of the base form controller from the extended form controller. I can see that the controller I create by extending the base controller is passed the application object:


init : function ( application ) {
console.log('initExtendedFormController', application);
this.callParent(application);
},

Fix:


init : function ( application ) {
console.log('initExtendedFormController', application);
this.callParent( [ application ] ); // or this.callParent(arguments);
},


It seems your problem is that BaseFormController.init() is called many times. Do you use a controller (extended from BaseFormController) for each form? Could you post your code of how you use controllers and forms?

jmozley
11 Apr 2012, 4:52 AM
I have extracted the relevant code to a smaller app that replicates the problem. Below is the base controller and the controllers of two forms that extend this. I do now see the application object passed to the base controller if I use this.callParent([application]) but I am not using this object in the init function of the base form.

The base controller is:



Ext.define('MyApp.controller.BaseFormController', {
extend: 'Ext.app.Controller',

init: function(application) {

console.log('BaseFormController', arguments);

this.control({
'#submit' : { click : this.submitForm },
'#close' : { click : this.closeForm }
});

},

/**
* Called after Submit button is clicked
*/
submitForm : function (button, event) {
// omitted for brevity
}, // eo function submitForm

/**
* Other handlers removed
*/

});


Two for controllers that inherit are as follows:



Ext.define('MyApp.controller.FormOneController', {
extend: 'MyApp.controller.BaseFormController',

views: [
'FormOne'
],

refs: [
{
ref : 'formOne',
selector : 'formOne',
xtype : 'form-one',
autoCreate : true
}
],


init : function (application) {

console.log('FormOneController', arguments);


this.callParent([application]);
},


actionDisplayView : function () {
//this.application.logIfConsole('function DisplayView');
this.application.setMainView(this.getFormOne());
}

});




Ext.define('MyApp.controller.FormTwoController', {
extend: 'MyApp.controller.BaseFormController',

views: [
'FormTwo'
],

refs: [
{
ref : 'formTwo',
selector : 'formTwo',
xtype : 'form-two',
autoCreate : true
}
],

init : function ( application ) {

console.log('FormTwoController', arguments);

this.callParent([application]);
},

actionDisplayView: function() {
this.application.setMainView(this.getFormTwo());
}

});

vietits
11 Apr 2012, 5:23 AM
Yes, this is what I guessed. The problem is that all of your controllers that extended from MyApp.controller.BaseFormController register to process 'click' event from component that match the selector '#submit' or '#close'. When user click on #submit button, all the event handlers of all controllers will be called. Because all the controllers share the same event handler (here is submitForm() from the base controller class) then they process event the same.

From the code of submitForm(), the form to be processed is always the parent of the #submit button, so all the controllers will process the same form.

To avoid this, you should modify selectors in <controller>.control() to reflex the form it manages. Something likes this


Ext.define('MyApp.controller.BaseFormController', {
extend: 'Ext.app.Controller',
formSelector: '', // <- this must be set by child classes
init: function(application) {
var me = this;
var control = {};
control[formSelector + ' #submit'] = {
click: me.submitForm
};
control[formSelector + ' #close'] = {
click: me.closeForm
};
me.control(control);
me.callParent(arguments);
},

/**
* Called after Submit button is clicked
*/
submitForm : function (button, event) {
// omitted for brevity
}, // eo function submitForm

/**
* Other handlers removed
*/


});


Child controller class


Ext.define('MyApp.controller.FormOneController', {
extend: 'MyApp.controller.BaseFormController',
formSelector: 'formOne',
views: [
'FormOne'
],


refs: [{
ref : 'formOne',
selector : 'formOne',
xtype : 'form-one',
autoCreate : true
}],

init : function (application) {
console.log('FormOneController', arguments);
this.callParent([application]);
},
actionDisplayView : function () {
//this.application.logIfConsole('function DisplayView');
this.application.setMainView(this.getFormOne());
}
});

Also, if there is no special in processing forms, you should use one controller to process all forms.

jmozley
11 Apr 2012, 5:52 AM
Thanks very much, this worked with the change shown below:



control[this.formSelector + ' #submit'] = {
click: me.submitForm
};
control[this.formSelector + ' #close'] = {
click: me.closeForm
};


Each form will need additional controllers beyond the standard for submission, close, etc. for instance to handle some linked combos. Hence wanting a controller for each form rather than one controller managing all forms.

Thanks again for the assistance :)