Listen for other components events - extjs

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);
}
}
}
})
}
})

Related

Extjs 4.2: 'hasListeners' property of Ext Component's(panel)

I have a panel with around 10 items. For all those items I have implemented "after render" event handler in respective controllers of each item.
This is how I have registered afterrender events in Controller's init method:
init: function() {
this.control({
'#myFirstPanel': {
afterrender: this.afterrender
}
});
}
My panel code is like:
Ext.define('App.view.popUpWindow', {
extend: 'Ext.panel.Panel',
initComponent: function(){
this.callParent();
},
id: 'popup',
layout: 'fit',
items: [
{
xtype: 'panel'
items: [
{
// One of my custom component
xtype:'FirstGrid'
}....//10 items in total
]
}]
});
When I get hasListeners property of my panel's object after my app loads by following code
Ext.getCmp('popup').hasListeners
It returns me this object
{afterrender: 10, tabchange: 1}
Now when I destroy my panel by this command
Ext.getCmp('popup').destroy()
and open my app again(my app is a sub Extjs app of a another Extjs app)
Ext.getCmp('popup').hasListeners
It returns me this object
{afterrender: 20, tabchange: 2}
My question is that why even after call to destroy(), hasListeners is some how keeping record of old Listeners? I know this object doesn't show the number of listener (I read api) but what is going here then?
Can someone explain this to me? As I have to manually remove old listeners(my other question about this), why not .destroy() is automatically destroying all references?
Thank you.

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');
}

Listening to events between view controllers

I am having trouble trying to figure out how to listen to events fired in one View Controller from another ViewController.
My grid component defines a Listener
selModel: {
listeners: {
selectionchange: 'onChemClick'
}
},
and my ViewController(called chemslist) has the function that fires another event
onChemClick: function(view, selected) {
console.log("before firing");
this.fireEvent('canvasData', this, selected.length);
console.log("after firing");
console.log(selected);
}
I have another controller that actually listens to this event and shows the data.
Ext.define('view.canvas.CanvasController', {
extend: 'Ext.app.ViewController',
alias: 'controller.canvas',
listen: {
controller: {
chemslist: {
canvasData: 'onCanvasData'
}
}
},
onCanvasData: function() {
console.log("At Fire");
}
});
For some reason I can't figure out why the CanvasController is not able to listen to the event. I did also go through the Ticket example and looked at how the events are fired and other viewControllers listen to them.
Also What would be a best practice if a selection on a grid in one region causes a lot of changes in another panel?, should the event be fired as a global so that all the components would listen to it ? or should i listen to it in the main Controller(not a ViewController) and generate the components based on the event ?
Thanks!
The docs say:
selectors are either Controller's id or '*' wildcard for any Controller.
Contrary to intuition, we are not supposed to assign any id or itemId to the controller we want to listen to but the id is automatically created by Application and the id equals to controller class name.
I've made simple example - button, 2 controllers C1 listening to button and C2 listening to C1.
Event handling is also working between ViewControllers, but you need to provide the controller's id in the listen config (as Saki stated), for your example the firing ViewController:
Ext.define('Edsp.view.chemslist.ChemsListController', {
extend: 'Ext.app.ViewController',
alias: 'controller.chemslist',
id: 'chemslist',
The listening ViewController looks like this:
Ext.define('Edsp.view.canvas.CanvasController', {
extend: 'Ext.app.ViewController',
alias: 'controller.canvas',
listen: {
controller: {
'#chemslist': {
canvasData: 'onCanvasData'
}
}
},
Small correction, id does not need a hash, in fact will not work with it, should be:
listen: {
controller: {
'chemslist': {
canvasData: 'onCanvasData'
}
}

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'.

Reusable Action in Ext JS MVC

I have a Grid Panel with a toolbar and an context menu.
The toolbar has a edit button and the context menu has a edit menu item.
Both shares the same properties (text, icon and handler)
Ext has something called Action which makes it possible to share functionality etc. between components, but til now I have had no success getting it to work in the MVC architecture
(I am using the new MVC architecture in 4.0)
My Action class looks like this:
Ext.define( 'App.action.EditAction', {
extend: 'Ext.Action',
text: 'Edit',
handler: function()
{
Ext.Msg.alert('Click', 'You did something.');
},
iconCls: 'icon-edit-user' ,
});
And in my context menu
requires: ['App.action.EditAction'],
initComponent: function()
{
var editUser = new App.action.EditAction();
this.items = [
editUser,
{
// More menuitems
}
...
];
this.callParent(arguments);
When running the code I get "config is undefined" in the console.
Can anyone point out what I am doing wrong?
Thanks in advance,
t
Passing an empty config to your constructor will avoid the error, but have unwanted consequences later because, unfortunately, the base class (Ext.Action) relies on this.initialConfig later on. For example, if you called editUser.getText() it would return undefined instead of the expected 'Edit'.
Another approach is to override your constructor to allow a no-arg invocation and apply your overridden configuration:
Ext.define( 'App.action.EditAction', {
extend: 'Ext.Action',
text: 'Edit',
constructor: function(config)
{
config = Ext.applyIf(config || {}, this);
this.callParent([config]);
},
handler: function()
{
Ext.Msg.alert('Click', 'You did something.');
},
iconCls: 'icon-edit-user' ,
});
As per Ext.Action constructor
constructor : function(config){
this.initialConfig = config;
this.itemId = config.itemId = (config.itemId || config.id || Ext.id());
this.items = [];
}
You must supply config not to get config is undefined exception in the second line (precisely in config.itemId part).
Updating your code as var editUser = new App.action.EditAction({}); should help(passing new empty object as config).
Surely, you could add some properties to the config object too.

Resources