Extending controller in ExtJS 4 MVC application - extjs4

I building my ExtJS 4 application following the MVC structure. I want to make an extendable grid MyGrid with some functionality that I can reuse several times. Therefore, I guess, it should have its own controller which is also extended, so that the functionality is inherited. How is this properly done?
In the code below I illustrate how I extend the controller MyGrid with MyExtendedGrid. I realize that I'm overriding the init function in the MyGrid controller, so that it is never called. Is the problem simply solved by calling the "super" init in MyGrid from MyExtendedGrid init, or merge the control objects? Is that the proper way to do this in the MVC spirit? If so, how?
controller/MyGrid.js :
Ext.define('App.controller.MyGrid', {
extend: 'Ext.app.Controller',
refs: [
{
ref: 'myGridView',
selector: 'mygrid'
}
],
init: function() {
var me=this;
me.control({
'mygrid textfield[name=searchField]': {
change: function() {
var view = me.getMyGridView();
// Do something with view
}
}
});
}
});
controller/MyExtendedGrid.js :
Ext.define('App.controller.MyExtendedGrid', {
extend: 'App.controller.MyGrid',
views: [
'grids.MyExtendedGrid'],
refs: [
{
ref: 'myExtendedGridView',
selector: 'myextendedgrid'
}
],
init: function() {
var me=this;
me.control({
'myextendedgrid': {
// Some control code
// Using getMyExtendedGridView()
}
});
}
});
view/grids/MyGrid.js :
Ext.define('App.view.grids.MyGrid', {
extend: 'Ext.grid.Panel',
alias : 'widget.mygrid',
requires: [
],
store: '', // Not defined here
columns: [ ], // Not defined here
initComponent: function() {
var me = this;
me.tbar = [
'Search',
{
xtype: 'textfield',
name: 'searchField',
hideLabel: true,
width: 150
}
];
me.callParent(arguments);
}
});
view/grids/MyExtendedGrid.js :
Ext.define('App.view.grids.MyExtendedGrid', {
extend: 'App.view.grids.MyGrid',
alias : 'widget.myextendedgrid',
store: 'MyStore',
columns: [
// ...
],
initComponent: function() {
var me = this;
me.bbar = [
//...
];
me.callParent(arguments);
}
});

It's actually a bit trickier...
Here is what we did in our application (we have exact same situation - some kind of base controller, that is reused in many different places)
Keep init function in base controller.
Define common base method in this base controller (like gridRendered - where you need to do something for all controllers all the time).
Subscribe to the events in all child controllers but subscribe events using methods of base controller. It won't work otherwise - base controller doesn't have proper refs to properly subscribed to events.
I can post couple source snippets, but I think it's pretty straightforward.

Ok, after some thinking I decided on the following solution, which has the advantage that mygrid does not need to know about myextendedgrid.
I extend my gridview as in the question.
I gave the base grid its own controller for doing common functionality, for instance deleteButton.setDisable(false) when something is selected in the grid.
Next, I remind myself that using refs:[ (for instance with selector: 'mygrid') would ambiguously point to both instances of the base class any extended instances. When using me.control({ I instead get the relevant grid by traversing from the activated element using up:
me.control({
'mygrid textfield[name=searchField]': {
change: function(searchfield) {
var grid=searchfield.up('mygrid'); // (mygrid or myextendedgrid!)
// Do something with view
}
}
...
The extended grid I give its own controller and here I could use refs. I don't extend the controller from the MyGrid class (but rather from 'Ext.app.Controller'), unless I would like to use functions or variables from the MyGrid controller. All the controllers are started from app.js using:
Ext.application({
controllers: [
'MyGrid'
'MyExtendedGrid',
],
...
In order to get the grid when rows are selected in the grid, I stored the grid in the selection model as below:
In controller/MyGrid.js :
me.control({
'mygrid': {
afterrender: function(grid) {
var selModel=grid.getSelectionModel();
selModel.myGrid=grid;
},
selectionchange: function(selModel, selected, eOpts) {
var grid=selModel.theLookupGrid;
// Do something with view
}
...

Related

Sencha Touch 2 - Pass value to view from controller is Error?

I want to use value from controller to filter store.I put this code:
My Controller:
showCatQuery: function(list,index,element,record){
var catid = record.get('id'); << Value to pass
this.getNavigation().push({
xtype: 'panel',
title: 'A',
scrollable: true,
styleHtmlContent: true,
catid: catid,
layout: {
type: 'fit'
},
items: [
{
xtype: 'showSearchCategory',
}
]
});
}
My view in initialize
this.callParent(arguments);
var sto = Ext.getStore('allapp');
sto.clearFilter();
sto.filter('categoryid', this.getCatid());
And this Error message:
Uncaught TypeError: Object [object Object] has no method 'getCatid'
Have you made sure, your View has a function called getCadid? This is what the error message is trying to tell you. You would have to make sure there is a method available while initializing.
Another, possible simpler approach is to filter the store in the controller - which would be the better approach from my perspective. The View would be able to just care about how anything is displayed and the controller cares about what data to display.
You can use references and controls to wait for the view to finish loading and then filter the store (or wait for the user to activate custom filters, etc.):
Ext.define('myApp.controller.aController', {
extend: 'Ext.app.Controller',
config: {
refs: {
justAName: 'ViewName'
}
control: {
justAName: {
activate: 'onActivateView'
}
}
}
onActivateView: function () {...}
});
This is the basic stub for a controller listening to the activation of a specific view. You can just get your store in this function and filter it by all data available in the controller. To get data from the View, create a reference to it, and access it in the controller via for example:
var data = this.getJustAnotherName().getValue();
//having a reference to a textfield for example called justAnotherName

List item disclosure isn't firing inside a controller

I'm trying to get the item disclosure to work in a list item in Sencha Touch using a controller ref. But the event never seems to fire/receive inside controller. All of the examples I've seen have the list item using a listener but I thought that wasn't very MVC so I'm trying to do it this way (is there any reason why they use listeners instead of a controller?)
In my view, the list is an item inside the EnquiryIndex view.
When I do this in a console window it returns the list correctly so I know the ref is working ok:
Ext.ComponentQuery.query("enquiryindexview list")[0]
See below example:
Ext.define('MyApp.controller.EnquiryIndex', {
extend: 'Ext.app.Controller',
requires: [
],
config: {
refs: {
enquiryIndexViewRef: 'enquiryindexview list'
},
control: {
'enquiryIndexViewRef': {
disclose: 'onDiscloseEnquiryIndex'
}
}
},
onDiscloseEnquiryIndex: function (rec) {
// never gets here!
}
});
Try this
config: {
control: {
'enquiryindexview list': {
disclose: 'onDiscloseEnquiryIndex'
}
}
or this
config: {
refs: {
enquiryIndexViewRef: 'enquiryindexview list'
},
control: {
enquiryIndexViewRef: {
disclose: 'onDiscloseEnquiryIndex'
}
}
Hope this helps
What you might have missed doing is adding this line in the app.js
controllers: ['EnquiryIndex'],
Try this out, it should work now.

How can I get a view in Sencha Touch 2 from within a controller by calling a function automatically generated by the ref config?

I have defined a controller in Sencha, that includes a refs attribute referencing my view, yet whenever I call the automatically generated "get" function to get the view, based on the refs attribute, it returns undefined. Here is my example:
I have the following controller in app/controller/Locals.js:
Ext.define('MobileUnion.controller.Locals', {
extend: 'Ext.app.Controller',
// Base class methods.
launch: function () {
this.callParent(arguments);
},
init: function () {
this.callParent(arguments);
},
config: {
refs: {
localsEditorView: 'localseditorview',
},
control: {
localsEditorView: {}
}
},
slideUpTransition: { type: 'cover', direction: 'up' },
onEditLocalsCommand: function() {
this.activateLocalsEditor();
},
activateLocalsEditor: function() {
var localsEditorView = this.getLocalsEditorView();
console.log(localsEditorView); // Returns "undefined" to console.
Ext.Viewport.animateActiveItem(localsEditorView, this.slideUpTransition);
}
});
I have the following view in app/views/LocalsEditor.js:
Ext.define('MobileUnion.view.LocalsEditor', {
extend: 'Ext.Panel',
alias: 'widget.localseditorview',
config: {
html: 'This is the new view which should show up on top!'
},
});
So, in the above example, if I call this.getLocalsEditorView() from within my controller, I get "undefined" even though I set a refs attribute as localsEditorView: 'localseditorview' and I defined MobileUnion.view.LocalsEditor to include an alias of widget.localseditorview. I feel like I should get the view when I do this.
By the way, I did define the view in the views attribute of my app.js, so that's not it.
Further information: there's no actual error being returned in my webkit console. Just the call to console.log() noted above in my controller returns undefined, rather than the view object.
Question: What do I have to do to make this function return the view, and not undefined? Any help would be appreciated. I've looked to make sure it's not just a typo; it does not seem to be.
The "refs" is to create references to existing components. So far all you've done is declare a class, from what you've posted you've never instanced it anywhere.
You might want to read the docs about autoCreate here:
http://docs.sencha.com/touch/2-1/#!/api/Ext.app.Controller-cfg-refs

ExtJS4 Controller not getting store (null)

I have a ProjectsController like:
Ext.define('SimpleTodo.controller.ProjectsController', {
extend: 'Ext.app.Controller',
refs: [
{ ref: 'addProjectPanel', selector: '#addProjectPanel' }
],
init: function() {
this.control({
'button[action=addProject]': {
click: this.addProject
}
});
},
addProject: function() {
var form = this.getAddProjectPanel().getForm();
if (form.isValid()) {
var projectsStore = Ext.data.StoreManager.lookup('projects'); // Am getting null here
projectsStore.add(form.getFieldValues());
projectsStore.sync();
} else {
Ext.Msg.alert('INVALID!');
}
}
});
I believe the problem is because the store has not been initialized yet? How then should I modify my code? My store is defined like:
Ext.define('SimpleTodo.store.Projects', {
extend: 'Ext.data.Store',
requires: [
'SimpleTodo.model.Project'
],
model: 'SimpleTodo.model.Project',
storeId: 'projects'
});
I encounted the same problem when trying to load my store, thus populating my Gridview/Table. I fixed it by moving the function to launch, wonder if thats the right way? In the docs, init is used
Ext.define('SimpleTodo.controller.SidebarController', {
extend: 'Ext.app.Controller',
...
launch: function() {
var projectsStore = Ext.data.StoreManager.lookup('projects');
projectsStore.load();
},
Add
stores:
[
'Projects'
],
to controller code and then you can use
getProjectsStore();
to refer to.
Have a look at http://docs.sencha.com/ext-js/4-0/#!/guide/mvc_pt3, 1. Getting References
Have u config a proxy to load the data?
Anyway place
models: [name of the model],
stores:[ name of the store],
into controller
Set into Store autoLoad:true;
Set a Proxy into Model to tell how load data into store.
Ref:
http://docs.sencha.com/ext-js/4-1/#!/api/Ext.data.proxy.Proxy

Define a reusable component

1-I used following code to define a reusable grid,
but when I make instance, no config in class definition either do not have effect of break the code. What is the reason?
3- Is there any restriction in class config declaration?
2- How I can make some default columns in grid class and add some more columns to its objects?
Thanks
Ext.define("IBS.users.Grid", {
extend: "Ext.grid.Panel",
config:{
selType:'checkboxmodel', //not work
dockedItems:[/* items */], //break
multiSelect:true,
features: [
{
groupHeaderTpl: '{name}',
ftype: 'groupingsummary'
},
{
ftype:'filters',
encode: false, // json encode the filter query
local: true
}
],
viewConfig: { //not work
stripeRows: true,
filterable:true,
loadMask: false
},
listeners : {
itemdblclick: function(dv, record, item, index, e) {
console.log(arguments);
}
}
},
constructor:function(config) {
this.callParent(arguments);
this.initConfig(config);
// this.self.instanceCount++;
}
});
1-I used following code to define a reusable grid, but when I make instance, no config in class definition either do not have effect of break the code. What is the reason?
I can answer why your config doesn't have effect. Because config which is being passed into cunstructor is not your default config. You have to apply your default config in order to make default config to have effect:
constructor:function(config) {
config = Ext.applyIf(config, this.config);
this.callParent(arguments);
this.initConfig(config);
}
However, I don't know why dockedItems:[/* items */] breaks the code. Maybe you have syntax or logical errors somewhere within /* items */.
2- How I can make some default columns in grid class and add some more
columns to its objects?
That is easy. Just override your initComponent function:
Ext.define("IBS.users.Grid", {
extend: "Ext.grid.Panel",
// ...
initComponent : function(){
if (!this.columns) {
// default columns:
this.columns = [{
dataIndex: 'dkjgkjd',
// ...
}];
// if we passed extraColumns config
if (this.extraColumns)
for (var i=0; i < this.extraColumns.length; i++)
this.columns.push(this.extraColumns[i]);
}
this.callParent(arguments);
},
// ...
});
3- Is there any restriction in class config declaration?
I'm not aware of any. However, I wouldn't recommend to declare object configs in class definition. For example:
Ext.define("IBS.users.Grid", {
extend: "Ext.grid.Panel",
bbar: Ext.create('Ext.toolbar.Toolbar', // ...
// ...
});
It will be ok with first instance of the class. But when you create the second instance it's bbar refers to the same object as the first instance. And therefore bbar will disappear from the first grid.
Instead declare object configs in initComponent.