Using more than one controller with ExtJS 4 MVC - extjs

Let's say I have a main controller, then my application has a controller for each "module". This main controller contains the viewport, then a header (with a menu) + a "centered" container, which is empty at the beginning.
A click in the menu will change the current module/controller and the adhoc view (which belongs to this controller) will be displayed in the centered container.
I think it's a very simple scenario, but strangely I didn't find the proper way to do it. Any ideas? Thanks a lot!

Here is what I do: I have a toolbar on top, a left navigation and the center location is work area (basically a tab panel) like you mentioned. Lets take each part fo the application and explain.First, here is how my viewport look like:
Ext.define('App.view.Viewport',{
extend: 'Ext.container.Viewport',
layout: 'border',
requires: [
'App.view.menu.NavigationPane',
'App.view.CenterPane',
'App.view.menu.Toolbar'
],
initComponent: function() {
Ext.apply(this, {
items: [{
region: 'north',
xtype: 'panel',
height: 24,
tbar: Ext.create('App.view.menu.Toolbar')
},{
title: 'Navigation Pane',
region: 'west',
width: 200,
layout: 'fit',
collapsible: true,
collapsed: true,
items: Ext.create('App.view.menu.NavigationPane')
},{
region: 'center',
xtype: 'centerpane'
}]
});
this.callParent(arguments);
}
});
You can see that I have a toolbar (App.view.menu.Toolbar) with menu and left navigation (App.view.menu.NavigationPane). These two, components make up my main menu or gateway to other modules. Users select the menu item and appropriate module views (like form, grid, charts etc) get loaded into the 'centerpane'. The centerpane is nothing but a derived class of Ext.tab.Panel.
Like you said, I have a main controller that handles all the requests from the toolbar and navigation pane. It handled only the toolbar and navigation pane's click actions. Here is my AppController:
Ext.define('CRM.controller.AppController',{
extend: 'Ext.app.Controller',
init: function() {
this.control({
'apptoolbar button[action="actionA"]' : {
click : function(butt,evt) {
this.application.getController('Controller1').displayList();
}
},
.
. // Add all your toolbar actions & navigation pane's actions...
.
'apptoolbar button[action="actionB"]' : {
click : function(butt,evt) {
this.application.getController('Controller2').NewRequest();
}
}
});
}
});
Look at one of my button's handler. I get hold of the controller through the 'application' property:
this.application.getController('Controller2').NewRequest();
With the help of getController method, I get the instance of the controller and then call any method inside my controller. Now lets have a look at the skeleton of my module's controller:
Ext.define('CRM.controller.Controller2',{
extend: 'Ext.app.Controller',
refs: [
{ref:'cp',selector: 'centerpane'}, // reference to the center pane
// other references for the controller
],
views: ['c2.CreateForm','c2.EditForm','c2.SearchForm','c2.SearchForm'],
init: function() {
this.control({
'newform button[action="save"]' : {
// Do save action for new item
},
'editform button[action="save"]' : {
// update the record...
},
'c2gridx click' : {
// oh! an item was click in grid view
}
});
},
NewRequest: function() {
var view = Ext.widget('newform');
var cp = this.getCp(); // Get hold of the center pane...
cp.add(view);
cp.setActiveTab(view);
},
displayList: function() {
// Create grid view and display...
}
});
My module's controller have only the actions related to that module (a module's grid, forms etc). This should help you get started with rolling in right direction.

In main controller add child controller in the click event handler of menu
Ext.define('MyAPP.controller.MainController',{
...
init:function()
{
this.control({
'menulink':
{
click:this.populateCenterPanel
}
});
},
populateCenterPanel:function()
{
this.getController('ChildController');
}
});
In launch function add listener to controller add event like this -
Ext.application({
...
launch:function(){
this.controllers.addListener('add',this.newControllerAdded,this);
},
newControllerAdded:function(idx, ctrlr, token){
ctrlr.init();
}
});
Now put code for dynamically embedding views in the viewport in init method of ChildController.
Ext.define('MyAPP.controller.ChildController',{
...
refs: [
{ref:'displayPanel',selector:'panel[itemId=EmbedHere]'}
]
init:function(){
var panel=this.getDisplayPanel();
panel.removeAll();
panel.add({
xtype:'mycustomview',
flex:1,
autoHeight:true,
...
});
}
});
HTH :)

Related

change listener doesnt work in extjs

I have a dynamic form and i want to add a listener when change a field value, but i couldnt achive to do that. I added a click listener but when i change it to the change it doesnt work.
thanks in advance.
here is the code below :
panel = Ext.define('MyApp.view.dynamicform.Form', {
extend: 'Ext.form.Panel',
alias: 'widget.dynamicformform',
title: title,
id: 'dynamicform.Form',
bodyPadding: 5,
autoScroll: true,
layout: 'auto',
defaults: {
anchor: '100%'
},
dockedItems: [],
items : genItems(storeData),
listeners: {
afterrender: function (comp) {
var element = comp.getEl();
element.on('change', function(e,el) {
alert('blabla')
});
}
},
initComponent : function() {
this.callParent(arguments);
}
});
when i write click instead of change it works perfectly. I dont get what iam doing wrong.
The afterrender event you have added the listener for is purely for the panel component alone. Therefore trying to attach a change event wont work, since you are trying to do this on the panel:
afterrender: function (comp) {
var element = comp.getEl();
//element is the panel here, not child items such as spinners...
element.on('change', function(e,el) {
alert('blabla')
});
}
You say the click is working, but I think that's just because you are clicking anywhere on the panel including on the child items you are rendering. Instead, the child items coming back in the genItems() need to contain change event listener configs.
EDIT
You could loop through the child items on comp in your afterrender event and for any that are spinners, etc, add the change events that way.

ExtJS 4.2.1 - Cannot get View instance from the controller

In my app I have the viewport with one item, a main view, which it is a simple class extending from Ext.container.Container.
I have a main controller too, and I'm trying to get the view instance, so dynamically I can push the corresponding items if the user is logged or not.
I've tried using views: ['MainView'], refs[ { selector: 'thextype' }], etc with no luck.
I was using the reference (ref) in sencha touch to do this kind of things, can you help me with Extjs v4.2 ?
Just for clarification, I'm not trying to get the DOM element, I'm trying to get the view instance with the associated methods.
Thanks in advance,
Define xtype of your view:
xtype: 'mainview'
and then in your controller:
requires: ['...'] // your view class
// ...
refs: [{
ref: 'mainView',
selector: 'mainview' // your xtype
}]
and then you can get the instance of your view in the controller with this.getMainView()
I've tried that without good results.
What I'm trying to do is something like. Based on your response should work
Ext.define('MyApp.view.MainView', {
extend: 'Ext.container.Container',
alias: 'widget.mainContainer',
cls: ['mainContainer'],
items: [
{
xtype: 'panel',
items: [
{
html: "my view"
}
]
}
]});
Ext.define('MyApp.controller.MainController', {
extend: 'MyApp.controller.BaseController',
refs: [
{
ref: 'mainContainer',
selector: 'mainContainer'
}
],
init: function() {
this.getApplication().on({
openDashboard: this.onOpenDashboard
});
},
onOpenDashboard: function() {
var mainContainerView = this.getMainContainer();
mainContainerView.showSomething(); //mainContainerView should be an instance of the view.
}});
Where, openDashboard event is fired if after a login success.
After some time debugging, it seems the problem was the context where it was being called the function.
I've added in the init method a line like:
var openCrmDashboardFn = Ext.Function.bind(this.onOpenCrmDashboard, this);
and it worked.
Thank you!

Extjs 4 MVC TabPanel - bad rendering tab

Please, help me. I am creating a TabPanel using the MVC. At the time of rendering panel add tab. Here is the code of the two views and controller. The result is a distorted picture.
The title of the tab is drawn twice: at the top and bottom (stretched to full screen). When I change in the controller function addNewTab() the line "tp.add({xtype: 'mytab'}).show();"
to the other "tp.add({title: 'new Tab', html: 'MVC New Tab Example'}).show();", then everything is rendered properly. I will be grateful for the help.
Views:
Ext.define('MY.view.Tab', {
extend: 'Ext.tab.Tab',
alias: 'widget.mytab',
initComponent: function() {
this.title = 'new Tab';
this.html = 'MVC New Tab Example';
this.callParent(arguments);
}
});
Ext.define('MY.view.TabPanel', {
extend: 'Ext.tab.Panel',
alias: 'widget.mytabpanel',
initComponent: function() {
this.id = 'MYTabPanel';
this.callParent(arguments);
}
});
Controller:
Ext.define('MY.controller.TabPanel', {
extend: 'Ext.app.Controller',
requires: ['MY.view.Tab'],
init: function() {
this.control({
'#MYTabPanel': {
render: this.addNewTab
}
});
},
addNewTab: function(tp) {
tp.add({xtype: 'mytab'}).show(); //it work bad
//tp.add({title: 'new Tab', html: 'MVC New Tab Example'}).show(); //it work good
}
});
Your mytab class extends Ext.tab.Tab. That means that this code tp.add({xtype: 'mytab'});
is adding a Tab as a main view for the TabPanel. A tab is created automatically for every panel or container you add to the tab panel and consists of just the button-like component at the top of the view.
That means that when you call the code above, a Tab is created and pushed into the TabPanel as a view, then the TabPanel creates another another Tab using the same title and attaches it to the top of the Tab you just added.
This code tp.add({title: 'new Tab', html: 'MVC New Tab Example'})' works because you are actually adding a panel (The default xtype used by Ext is panel) and the TabPanel is creating the tab for you using the title you've provided.
To fix: make MY.view.Tab extend Panel instead of Tab. If you're trying to customize just the tab for a particular view, dig into the source for Ext.tab.Panel and check out the undocumented tagConfig property that you can add to TabPanel items.

Sencha Touch Navigation View - Changing Title does not work on Back Button

I have a Tab Panel as my initial item inside a Navigation View. When I change tab, I'm updating the Title in the Navigation Bar via:
activeitemchange: function(container){
var navigationView = container.up('navigationview'),
navigationBar = navigationView.getNavigationBar(),
newTabTitle = value.tab._title;
navigationBar.setTitle(newTabTitle);
}
The problem is that when I push a new view onto the Navigation View, the Text for the Back Button uses the old/original Title, and not the updated Title. Clicking the Back Button also sets the Navigation View Title to the old/original Title.
The closest I got to finding a solution was this:
http://www.sencha.com/forum/showthread.php?189284-Navigation-View-Title-(IPad-App)
But I get an 'undefined' error on the navigationBar.refreshProxy() call, so I'm assuming that only works for an older version of ST.
Any help would be great,
Thanks.
I don't know if you found any answer for this question or solved it by your own since question is quite old. But I tried what you wanted to do and I managed to get the result successfully. So here's the code. I'm following MVC strictly so posting necessary files that need this to work. Basically, views and controllers.
Main.js containing TabPanel
Ext.define('SO.view.Main', {
extend: 'Ext.tab.Panel',
xtype: 'main',
requires: [
'Ext.TitleBar',
'Ext.Video'
],
config: {
tabBarPosition: 'bottom',
items: [
{
title: 'Welcome',
iconCls: 'home',
styleHtmlContent: true,
scrollable: true,
html: [
"You've just generated a new Sencha Touch 2 project. What you're looking at right now is the ",
].join("")
},
{
title: 'Get Started',
iconCls: 'action',
items: [
{
xtype: 'button',
text: 'Push a new view!',
action:'push_new_view'
}
]
}
]
}
});
Nav.js having navigation view and default item as above tab panel
Ext.define('SO.view.Nav', {
extend: 'Ext.NavigationView',
xtype: 'nav',
config:{
fullscreen: true,
items: [
{
title: 'Default View',
xtype:'main'
}
]
}
});
And finally, controller. I did every user interaction handling in controller itself.
Ext.define('SO.controller.Nav',{
extend:'Ext.app.Controller',
config:{
refs:{
views:['SO.view.Main','SO.view.Nav'],
navView:'nav',
tabView:'main'
},
control:{
tabView:{
activeitemchange:'changeTitle'
},
'button[action=push_new_view]':{
tap:'pushNewView'
}
}
},
changeTitle:function(container,value,oldValue,eOpts ){
console.log(value.title);
this.getNavView().getNavigationBar().setTitle(value.title);
},
pushNewView:function(){
var activeTabTitle = this.getTabView().getActiveItem().title;
var controller = this;
controller.getNavView().push({
title: 'Second',
html: 'Second view!'
});
controller.getNavView().getNavigationBar().getBackButton().setText(activeTabTitle);
controller.getNavView().getNavigationBar().getBackButton().on('tap',function(self){
controller.getNavView().getNavigationBar().setTitle(activeTabTitle);
});
}
}
);
As you can see, I've attached function that changes title according to selected tab in changeTitle function.
the function pushNewView pushes new view and let's you ovverride back button behavior on tap. What I did is simply, get activeItem() from tab panel which holds a button that pushes new view. Once we got activeItem we can get it's title and that title need to be set to backButtnoText. So I traverse navigation view and getBackButton instance and simply by calling setText() method changed back button text.
Same time, I've attached event handler to back button, as we do want to change navigation bar title to previous title. So once, use taps back button, we set back the title to title we got in above step. You might want to detach event handler once you're done with eveything as it might cause problems or I'd rather say it'd be good.
Just try this, it just works.
You will need to change the title of the parent viewport, not just to the navigation view tile. Basically the Navigation title is already changing by itself based on the parent view title, and all pushed component title.
var view = Ext.create('Ext.NavigationView', {
fullscreen: true,
items: [{
title: 'Change this title ',
items: [{
xtype: 'button',
text: 'Push a new view!',
handler: function() {
view.push({
title: 'Second Title',
html: 'Second view!'
});
}
}]
}]
});
It should look something like this:
activeitemchange: function(container){
var newTabTitle = value.tab._title;
container.setTitle(newTabTitle);
}
//viewAppt is the reference of the view
//Use this
viewAppts.query('#headerTitlebar')[0].setTitle('Title');
// Instead of this
this.getApptsHeaderTitlebar().setTitle('Title');

Extjs: Add Link to container

I'm trying to create a specialized class which should contain, among other things, a link and a image.
I have something like:
Ext.define('Macros.app.ribbonAction', {
extend: 'Ext.Component',
//extend: 'Ext.panel.Panel',
alias: 'widget.ribbonAction',
initComponent: function () {
Ext.apply(this, {
items:[
{}
]
}
);
this.callParent(arguments);
}
});
What's the best way to add a link (bound to a javascript function) to the items collection?
The closest I can find is a button, but I'd really prefer a good old-fashioned link.
(I'm using ExtJs 4)
How about standard box?
{
xtype: 'box',
id: 'myLinkId',
autoEl: 'Link'
}
And add this to the container.
The only problem is that you need to assign event handler for 'click' event and you can do this only after element has been rendered.

Resources