Extjs MVC - having a special cotroller per tab in tabpanel - extjs

I have a MainController that listens to events in the entire app (globally).
My app has tab panel, and each panel is very complex and need to have its own controller (a lot of evets).
How will I add a special controller per tab panel to handle its own events?
If youll see bellow. I have the MainController opening new tabs in the method onMenuItemClick.
I want the MainController let other controllers handle the tab opening and tab event listening.
So for example.
MainController call CustomerController.
CustomerController listen to events on the customer tab only.
CustomerController extend BaseTabController.
BaseTabController listen to similar events across all tabs and handle tab opening.
This is what I have so far:
Ext.application({
name: 'App',
controllers: ['MainController'],
stores : ['MainMenuStore'],
autoCreateViewport: true,
appFolder: '/app',
});
Ext.define('App.controller.MainController', {
extend: 'Ext.app.Controller',
refs: [{ ref: 'tabs', selector: 'viewport > #tabs'}],
init: function () {
this.control({
'viewport > #nav': {
itemclick: this.onMenuItemClick
}
});
},
onMenuItemClick: function (view, rec) {
var id = rec.raw.panel;//Can be the controller name for example
var cls = "App.view." + id;
var tabs = this.getTabs();
var tab = tabs.child('#' + id);
console.log(tab);
if (!tab) {
tab = tabs.add(Ext.create(cls, {
itemId: id,
title: rec.get('text')
}));
}
tabs.setActiveTab(tab);
}
});
this is the basic uml that i want to achieve.

Thats exactly what DeftJS is for.
With DeftJs you can give each view its own controller. This will be applied for each instance of your views. Also, your special controllers can inhert from your base controller.
Check out the docs at DeftJS Docs
Example:
View:
Ext.define( 'MyProject.view.tab.Main', {
extend: 'Ext.tab.Panel',
controller: 'MyProject.controller.tab.Main',
...
Controller:
Ext.define( 'MyProject.controller.tab.Main', {
extend: 'Deft.mvc.ViewController',
...

Related

Firing custom event from custom component and handle event in viewController

I have created a custom component that extends from Ext.Panel. I have added a click listener to the custom component so that when it's clicked it will fire an event. I am instantiating the custom component in a view and I want to handle the event thats fired from the custom component in the viewController associated with that view.
However, when I fire the event, it's not bubbling up to the viewController. Is there a way to fire an event on the global scope? How do I go about handling an event in a viewController where the component that fires the event is instantiated in the view associated with the view controller?
My custom component looks somthing like so:
Ext.define('MyApp.ux.CustomComponent', {
extend: 'Ext.Panel',
xtype: 'custom-component'
initComponent: function() {
var me = this;
me.callParent();
me.addListener({
'render': function(panel) {
panel.body.on('click', function() {
me.fireEvent('customEventName');
});
}
});
}
});
I am instantiating my custom component in a view like so:
Ext.define('MyApp.view.main.Main', {
extend: 'Ext.container.Container',
controller: 'main'
items: [{
xtype: 'custom-component'
}]
});
And in my viewController (for the view that im instantiating my custom component in) I have the following listener:
customEventName: function () {
console.log('I have been fired');
}
View controllers listen for child item listeners, but not manually fired events. So, you need to use listener config for this like this e.g.
Ext.define('MyApp.view.main.Main', {
extend: 'Ext.container.Container',
controller: 'main'
items: [{
xtype: 'custom-component',
listeners: {
customEventName: 'customHandlerNameInController'
}
}]
});
Now when you fire your custom event, your view controller method must work.
To fire events globally, you can use:
Ext.GlobalEvents.fireEvent('eventName', {args});
http://docs.sencha.com/extjs/6.0/6.0.0-classic/#!/api/Ext.GlobalEvents-method-fireEvent
Edit:
You can try a workaround:
Ext.GlobalEvents.fireEvent('customEventName');
In your controller:
listen: {
global: {
'customEventName': 'onClick'
}
}
onClick: function(){
Ext.log('click happened');
}

How to recompile a widget's templateUrl in Malhar AngularJS dashboard framework

I'm using the Malhar AngularJS Dashboard Framework located here on github: https://github.com/DataTorrent/malhar-angular-dashboard , and a simple demo here: http://datatorrent.github.io/malhar-angular-dashboard/#/ .
Their system uses JQuery Sortable Widgets, which allows me to load up some charts and grids into a dashboard.
The way I load my charts and grids is by using their templateUrl property on the widget definition, as follows :
{
name: myWidgetEnum.TREELIST,
title: 'Tree List',
templateUrl: 'app/shared/gadget-templates/tree-list.html'
}
Here's a more complete sample of the Malhar Widget Definitions, which is where I define my UI "gadgets" to be rendered within the sortable widgets :
(function () {
'use strict';
angular.module('rage')
.factory('widgetDefinitions', ['ColumnChartDataModel', 'TreeListDataModel', 'TreeMapDataModel', 'GridDataModel', widgetDefinitions])
var myWidgetEnum = Object.freeze({
TREELIST: 'chart_treelist',
TREEMAP: 'chart_treemap',
HEATMAP: 'chart_heatmap',
GRID: 'chart_grid'
});
/* Define the widget definitions - base properties are defined in WidgetModel factory.
*/
function widgetDefinitions(ColumnChartDataModel, TreeListDataModel, TreeMapDataModel, GridDataModel) {
var size = { width: '20%', height: 90 };
var fixedSize = { width: '50%', height: 320 }; // keep height as a number, and add 'px' in directive
var initImagePath = 'images4/256x256/';
return [
{
name: myWidgetEnum.TREELIST,
title: 'Tree List',
templateUrl: 'app/shared/gadget-templates/tree-list.html',
dataModelType: TreeListDataModel,
dataModelOptions: {}, // this property will be persisted in DashboardState factory
gadgetType: 'table',
chartType: 'treelist',
settingsModalOptions: { }
},
{
name: myWidgetEnum.HEATMAP,
title: 'Heat Map',
templateUrl: 'app/shared/gadget-templates/grid.html',
dataModelType: GridDataModel,
dataModelOptions: {}, // this property will be persisted in DashboardState factory
gadgetType: 'table',
chartType: 'heatmap',
settingsModalOptions: {} // see 'MainCtrl' controller
}
];
}
})();
And the dashboard options in my main controller :
$scope.dashboardOptions = {
widgetButtons: false, // 'false' makes it a dropdown of options
widgetDefinitions: widgetDefinitions, // see dashboard-widget-factory.js
defaultWidgets: defaultWidgets,
storage: $window.localStorage,
storageId: $scope.defaultDashboardId, // 'rage.ui' - no longer hard code the storage Id - 06/11/2015 BM:
hideWidgetSettings: false,
settingsModalOptions: {
templateUrl: 'app/shared/include/gadget-config-new/gadget-config-container.html',
controller: 'GadgetSettingsCtrl_NEW as settings',
backdrop: true
},
onSettingsClose: function (result, widget) {
jQuery.extend(true, widget, result);
widget.dataModelOptions = result.dataModelOptions;
},
onSettingsDismiss: function (reason) {
console.log('widget settings were dismissed. Reason: ', reason);
}
};
When Malhar first loads up the widgets, it compiles the templateUrl within the JQuery sortable widget.
However, if I launch the widget settings modal and choose a different "widget" from the Widget Definitions list, I'm having an issue when I try to recompile the new templateUrl.
In other words if the first widget loaded up is the TREELIST, then in it's modal settings I choose a new widget called HEATMAP, I need to somehow force a recompile on the new html template that was loaded by Malhar.
Here on github, you can find the main directive, malhar-angular-dashboard.js https://github.com/DataTorrent/malhar-angular-dashboard/blob/master/dist/malhar-angular-dashboard.js , and if you search down for the 'DashboardWidgetCtrl' controller you'll find the $scope.makeTemplateString function as well as the $scope.compileTemplate function further below that.
I'm experimenting with the resolve section of the $modal instance as follows, but this seems to be more of a hack than an elegant solution:
See the angular.element below inside the try...catch section :
// Set resolve and reject callbacks for the result promise
modalInstance.result.then(
function (result) {
// Call the close callback
onClose(result, widget, scope);
// CHECK IF FIRST TIME CONFIGURING THE GADGET - 04/22/2015 BM:
if (!widget.gadgetConfigured) {
widget.gadgetConfigured = true;
widget.setHeight(widget.fixedSize.height);
widget.setWidth(widget.fixedSize.width);
}
scope.$broadcast('widgetSettingsClosed', widget, result);
//AW Persist title change from options editor
scope.$emit('widgetChanged', widget);
// hack to recompile the widget if user has swapped gadget types in gadget config modal - 10/01/2015 BM:
if (widget.chartType == "heatmap" || widget.chartType == "treelist") {
try {
angular.element(document.getElementById('dash')).scope().$$childHead.compileTemplate();
} catch (e) {
console.log("In modalInstance.result.then, cannot compile template " + e);
}
}
},
function (reason) {
// Call the dismiss callback
onDismiss(reason, scope);
}
);
If there's anyone with experience in Mahlar Dashboard, or in compiling directives, I would appreciate any guidance in this area.
thank you,
Bob

Listen for other components events

I am developing a component that act as an application taskbar.
Currently, I have a class App (that's not the fully qualified class name) that extends Ext.window.Window which on init it creates a button with reference to itself and renders it to the taskbar. But I don't think this is the application's responsibility to add itself to the taskbar, but rather it is the taskbar's responsibility to listen for applications initialization and create a reference to them in it.
So, in the taskbar's ViewController I need to capture all the render events fired by any App instance. I can't find a way to do that in the documentation.
How can I do it? Or is there a better way of doing it?
ExtJS 5.1
Define an Ext.app.EventDomain to monitor Ext.window.Window events.
Ext.define('MyApp.app.domain.Taskbar', {
extend: 'Ext.app.EventDomain',
singleton: true,
requires: [
'Ext.window.Window'
],
// catalog the domain in the Ext.app.EventDomain.instances map
type: 'taskbar',
idProperty: 'id',
constructor: function() {
this.callParent();
this.monitor( Ext.window.Window );
}
})
Define a controller to listen for the window render event.
Ext.define('MyApp.controller.Taskbar', {
extend: 'Ext.app.Controller',
requires : [
'MyApp.app.domain.Taskbar'
],
init: function() {
this.listen({
taskbar: {
// wildcard selector to match any window
'*':{
render: function(window, eOpts){
console.log('render window: ' + window.id);
}
}
}
})
}
})

ExtJS: Selector for all sub-views of a view

Setup: So I have a controller that manages a hierarchy of views. I want my controller to be able to pickup on any events fired within this view hierarchy.
app/view/myView.js
Ext.define('app.view.myView', {
...
alias: 'widget.myview',
buttons: [{
...
handler: function() {
this.fireEvent('someEvent', this, args); // handler in controller
},
...
}]
});
app/controller/myController.js
Ext.define('app.controller.myController', {
...
views: ['myView', ...],
init: function() {
this.control({
'someSelector': { // what does this selector need to be?
someEvent: //handle event
},
...
});
}
});
What does 'someSelector' need to be to accomplish this?
The selectors work in a very similar manner to CSS selectors:
myView *
Any child element at any depth under myView.

getRootNode() is not a function

I try to develop an app with MVC architecture. I've the following Controller code:
Ext.define('PM.controller.Projects', {
extend: 'Ext.app.Controller',
models: ['Project'],
stores: ['Projects'],
views: [
'projects.Tree',
'Toolbar',
],
init: function(config) {
var tree = this.getProjectsTreeView();
var rootNode = tree.getRootNode();
console.log(rootNode);
this.callParent(config);
}
});
And this view code:
Ext.define('PM.view.projects.Tree', {
extend: 'Ext.tree.Panel',
xtype: 'projectsTree',
title: 'Projects',
hideHeaders: true,
root: {
text: "Projekte"
}
});
It try to get the root node from my tree view in the controller but I get the error that getRootNode() is not a valid function in my controller. Can anybody tell me why I get this error? My target is to add new children to this root node from an ajax request.
Thanks
The methods Ext generates for each string in the views array return constructors that can be used to create the respective views. That seems bizarre, but that's how it is.
If you want to access the actual view component, you'll need to create a ref for it. Your init method should not assume that the view exists yet. It's very likely that it won't since the controller's init method is called before the application's launch method which is probably where all the views are getting added to the page.
You want to put your logic in the controller's onLaunch template method which is called after the application has been launched and your view has been added.
Ext.define('PM.controller.Projects', {
extend: 'Ext.app.Controller',
refs: [{
ref: 'projectsTreeView',
selector: 'projectsTree'
}],
init: function() {
// It's safe to add selectors for views that don't exist yet.
this.control(/*...*/)
},
onLaunch: function(config) {
var tree = this.getProjectsTreeView();
var rootNode = tree.getRootNode();
console.log(rootNode);
}
});
If this doesn't work, that means you aren't actually adding your view anywhere. One place you could add it is in the application's launch method. Something has to add the treeview.
Ext.application({
// ...
views: ['projects.Tree']
launch: function() {
Ext.create('Ext.container.Viewport', {
layout: 'fit',
items: [new this.getProjectsTreeView()]
});
}
});
So the chronology of events is this:
Application#constructor
Controller#constructor
Controller#init (can't assume the view exists)
Application#onBeforeLaunch
Application#launch (view is now added)
Controller#onLaunch (do something with the view that is now available)
Also, your view alias may need to be 'widget.projectsTree' not just 'projectsTree'.

Resources