I'm new to Extjs library and I cannot figure out how to add listener to some events in GridPanel. As I'm filling the grid asynchronously I want my function to be executed when we add new element to grid panel.
I found (I think) correct event:
added( Ext.Component this, Ext.container.Container container, Number pos )
but I cannot find a correct place to put the listener as this function is executed only once on the page load:
Ext.define('MyApp.NewsGrid', {
extend: 'Ext.grid.GridPanel',
alias: 'widget.newsgrid',
initComponent: function() {
Ext.apply(this, {
title: 'News',
store: newsStore,
viewConfig: {
plugins: [{
pluginId: 'preview',
ptype: 'preview',
bodyField: 'testo',
expanded: false
}],
listeners: {
add: function() {
alert("add executed");
},
added: function() {
alert("added executed");
}
}
},
....
The problem is that there are different ways to add listeners (inside and outside viewConfig hash, inside and outside component definition etc...) and I cannot figure out one that works in this case. What makes it even more frustrating is that there are many places in the internet with documentation for version 3 or even without specifying the version.
You should listen the add event of Store:
initComponent: function() {
// somewhere inside initComponent...
this.getStore().on("add", function(store, records) {
alert(records.length + " records added");
}, this);
}
Related
I am creating a page which will dynamically generate collapsed panels. When a user expands these panels, it will perform a GET request and populate this generated panel with the JSON response. The idea is to perform a sort of lazy-load or as-needed load, as the amount of data that would be shown initially can get overwhelming.
However, I can't seem to get the listeners for my panels to work.
Here is the code, which generates the panels through a button's click function:
xtype : 'button',
listeners : {
click : function (button, e, eOpts) {
console.log("Click function");
Ext.Ajax.request({
url: 'data/Countries.json',
success: function(response, options) {
var data = Ext.JSON.decode(response.responseText).results;
var container = Ext.getCmp('panelContainer');
container.removeAll();
for (i = 0; i < data.length; i++)
{
container.add({
xtype: 'panel',
title: 'Country Name - ' + data[i].countryName,
collapsible: true,
listeners: {
expand: function() {
Ext.Ajax.request({
url: 'data/CountryData.json',
success: function(response, options) {
var data = Ext.JSON.decode(response.responseText).results;
var me = this;
me.add({
xtype: 'grid',
store: Ext.create('Ext.data.Store',
{
fields : [{
name: 'gdp'
}, {
name: 'rank'
}, {
name: 'founded'
}, {
name: 'governor'
}, {
name: 'notes'
}], //eo fields
data: data.information,
}),// eo store
columns: [
{ text: 'GDP', dataIndex: 'gdp'},
{ text: 'rank', dataIndex: 'rank'},
{ text: 'Date', dataIndex: 'founded'},
{ text: 'name', dataIndex: 'governor'},
{ text: 'Notes', dataIndex: 'notes', flex: 1, cellWrap: true}
], //eo columns
autoLoad: true
});
},
failure: function(response, options) {}
});
},
collapse: function() {
console.log("Collapse function");
var me = this;
me.removeAll();
}
}//eo panel listeners
});//eo cont.add()
}//eo for loop
}, //eo success
failure: function(response, options) {
//HTTP GET request failure
}//eo failure
});//eo Ajax request
} //eo click
}//eo button listeners
Originally, the panels were dynamically generated along with their populated grids from the click event, which worked perfectly. By wrapping the grid creation in a listener on the dynamically generated panel to create a load-as-needed, I can't get the expand or collapse listeners to trigger.
Searching around, one possible solution I haven't tried is to create a custom component and call it through its xtype rather than build everything in-line, which would let me define listeners there instead of nesting them in a function (this is better as well for readable and reusable code, but I'm just trying to get to the root of the issue for now).
Is there an issue with listeners on dynamically generated panels? What is the reason that the event triggers for collapse and expand aren't firing?
Thanks for all the help!
I'm still have a few issues, but as my main question was about firing the listeners, I'll write the solution I reached.
The issue I had was getting listeners to fire in a dynamically generated element. This led to nested listener functions, and I hadn't defined a scope. I had tried pagep's solution of setting the defaultListenerScope, but for me personally I didn't see a change.
I instead wrapped the listener functions into their own functions and called then through the listener like this:
listeners: {
expand: 'expandFunction',
collapse: 'collapseFunction'
},//eo panel listeners
expandFunction: function() {
//Do Ajax request and add grid to panel
},
collapseFunction: function() {
//Remove all child elements from this panel
}
Instead of doing this:
listeners: {
expand: function() {
//Do Ajax request and add grid to panel
},
collapse: function() {
//Remove all child elements from this panel
}
},//eo panel listeners
By wrapping the info this way, I was able (to a certain degree) to remove the nesting of listeners with generated elements. I also created a custom component and placed these listeners with the component I was generating. My only issue now is populating the generated element, since I am getting Uncaught TypeError: Cannot read property 'add' of undefined when trying to reference the itemId of my component.
My final simplified code, which generates a collapsed panel on button-click and populates it with generated data when expanded, looks like this:
//View.js
click: function (button, e, eOpts) {
Ext.Ajax.request({
url: 'data/Countries.json',
success: function(response, options) {
var data = Ext.JSON.decode(response.responseText).results;
var container = Ext.getCmp('panelContainer');
console.log(container);
container.removeAll();
for (i = 0; i < data.length; i++)
{
container.add({
xtype: 'customPanel',
title: data[i].country
});
}
});
//customPanel.js
Ext.define('MyApp.view.main.CustomPanel', {
extend: 'Ext.panel.Panel',
alias: 'widget.customPanel',
xtype: 'panel',
collapsible: true,
collapsed: true,
listeners: {
expand: 'expandFunction',
collapse: 'collapseFunction'
},//eo panel listeners
expandFunction: function() {
//Do Ajax request and add grid to panel
},
collapseFunction: function() {
//Remove all child elements from this 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.
This is the controller code:
Ext.define('XXX.controller.XXX', {
extend: 'Ext.app.Controller',
config: {
views: ['CustomView','CarouselView'],
refs: {
custom: "carouselview #customid"
},
control: {
custom: {
initialize : function() {
alert("it's loading")
}
}
}
},
launch: function(){
Ext.Viewport.add(Ext.create('XXX.view.CustomView'));
console.log(this.getCustom()) // ——> This works, it is not undefined
}
});
and this is the carousel view code:
Ext.define('XXX.view.CarouselView', {
extend: 'Ext.Carousel',
xtype: 'carouselview',
defaults: {
styleHtmlContent: true
},
config:{
direction: 'horizontal',
items: [
{
xtype: 'customview',
itemId: 'customid'
}
]
}
});
Now it's the customview :
Ext.define('XXX.view.CustomView', {
extend: 'Ext.Panel',
xtype: 'customview',
config: {
tpl: XXX
}
});
in the controllers's launch function, it can log the right value, but the initialize event can't be triggered.
And if i change refs to { custom: "customview" }, the initialize event can be triggered.
IMHO you (and someone answered below) misunderstand the use of itemId config.
Here is the difference between id and itemId:
id is the global identifier of a component. It can be used directly as a selector in Ext.ComponentQuery class which refs uses behind the scene. So if you want something like "carouselview #customid", you have to use id instead of itemId.
itemId is the global identifier within a class from which the component derives from. For example, assume that you have an Ext.Button with itemId: "myCustomButton", then you can have access to it via this refs: button#myCustomButton (please note that there's no space between them). This way, Ext.ComponentQuery first looks for all components xtyped button, then find the instance with that itemId.
So, if you want to use some string as "first-class" selector, you will have to use id. If you want to use itemId, you may want to always include its xtype before the itemId. Therefore, 2 possible solutions are:
First solution (still use itemId): custom: "carouselview customview#customid"
Second solution: keep your refs, but change #customid from itemId to id
Hope this helps.
UPDATE:
Just figured out that you are trying to initialize on something that get's the itemId on initialize :) Sorry, took me some time.
Basically the fireEvent('initialize') has already been in the past when you are trying to listen to it in the controller.
Use the xtype to initialize or simply:
Ext.define('XXX.view.CustomView', {
extend: 'Ext.Panel',
xtype: 'customview',
config: {
tpl: XXX
},
initialize: function() { // good way to use initialize inside the view, as it belongs to the view and there is not user input handled
}
});
OR
Ext.define('XXX.controller.XXX', {
extend: 'Ext.app.Controller',
config: {
views: ['CustomView','CarouselView'],
refs: {
custom: ".carouselview .customview" // --> HERE use this
},
control: {
custom: {
initialize : function() {
alert("it's loading") // Yeah, now you are getting here
}
}
}
},
launch: function(){ // --> this will be the same as if you are placing it in app.js launch
Ext.Viewport.add(Ext.create('XXX.view.CustomView')); // --> here the initialize happends and this.getCustom() does not yet exists
console.log(this.getCustom()) // ——> here this.getCustom() exists
}
});
Update 2: This confirms the cpuchartclicked event is being fired, because the alert('hello') works. What I need is to be able to respond to that event in my controller.
items: [
{
xtype: 'image',
itemId: 'graphiteChartCPU',
src: '',
listeners:{
'afterrender':function (comp) {
comp.getEl().on('click', function (el) {
this.fireEvent('cpuchartclicked');
}, this);
},
'cpuchartclicked':function () {
alert('hello');
}
}
}
]
Update: With the following, I am setting the scope for the on click handler. fireEvent() seems to be working now, but still not hearing the event in controller.
items: [
{
xtype: 'image',
itemId: 'graphiteChartCPU',
src: '',
listeners:{
'afterrender':function (comp) {
comp.getEl().on('click', function (el) {
alert('on click handler');
this.fireEvent('cpuchartclicked');
}, this);
}
}
}
]
// Listen Application Event
me.application.on({
cpuchartclicked: me.onChartClicked,
scope: me
});
I'm trying to fire a custom event on an image el so when the image is clicked the controller hears the event.
But I get an error cmp.fireEvent() is not a function?
items: [{
xtype: 'image',
itemId: 'graphiteChartCPU',
src: '',
listeners:{
'afterrender':function (comp) {
comp.getEl().on('click', function (el) {
el.fireEvent('cpuchartclicked');
});
}
}
}]
me.application.on({
cpuchartclicked: this.onChartClicked,
scope: this
});
You are confusing Components and Elements. The afterrender event listener is set on the Image Component, and receives the Component itself as the first argument (that you named el instead, incorrectly).
Then, in the afterrender listener, you retrieve the main DOM element for that Component, which happens to be an <img> tag, and set click listener on the element object, which is an instance of Ext.dom.Element.
The click event signature is not what you expect; the first argument is an Ext.util.Event object that does not have a fireEvent method. Hence the error.
I would suggest looking up event signatures in the docs before using them. Also try to add a debugger statement before the line that blows up, and see what variables are passed in and what is going on. Using Chrome or Firefox debugger can be immensely helpful here.
Solved it, though I am wondering if this is the best way to do it.
I fire the custom event via the image, and then listen for that event on the image as well in the controller.
items: [
{
xtype: 'image',
itemId: 'graphiteChartCPU',
src: '',
listeners:{
'afterrender':function (comp) {
comp.getEl().on('click', function (el) {
this.fireEvent('cpuchartclicked');
}, this);
}
}
}
]
'myContainer image':
{
'cpuchartclicked': me.onChartClicked
}
you must bind to the image element like that:
{
xtype: 'image',
itemId: 'graphiteChartCPU',
src: '',
listeners: {
el: {
click: function() {
console.log("Click");
}
}
}
}
it solve my problem in ext5
I want to add event listener to plugin on Controller.like http://docs.sencha.com/ext-js/4-1/#!/api/Ext.app.Controller It seems different to get plugin using component query than normal component.
Is it possible to get plugin from component using component query?
Here is my component
Ext.define('App.view.file.List',{
rootVisible: false,
extend:'Ext.tree.Panel',
alias:'widget.filelist',
viewConfig: {
plugins: {
ptype: 'treeviewdragdrop',
allowParentInsert:true
}
},
//etc ...
Can i get treeviewdragdrop plugin using component query like
Ext.define('App.controller.FileManagement', {
extend:'Ext.app.Controller',
stores:['Folder'],
views:['file.List','file.FileManagement'],
refs:[
{ ref:'fileList', selector:'filelist' }
],
init:function () {
this.control({
'filelist > treeviewdragdrop':{drop:this.drop} // <-- here is selector
});
},
// etc ....
You can't because a plugin is not a component, thus no selector will find it.
Also, the drop event is fired by the treeview, so the treeview is really what you want to hook to.
This will work:
init:function () {
this.control({
'filelist > treeview': {drop:this.drop}
});
},
There is no straightforward approach to do that. If I were in your shoes I would, probably, made tree to fire needed event when the plugin fires its event:
// view
Ext.define('App.view.file.List',{
// ...
viewConfig: {
plugins: {
ptype: 'treeviewdragdrop',
pluginId: 'treeviewdragdrop', // <-- id is needed for plugin retrieval
allowParentInsert:true
}
},
initComponent: funcion() {
var me = this;
me.addEvents('viewdrop');
me.callParent(arguments);
me.getPlugin('treeviewdragdrop').on('drop', function(node, data, overModel, dropPosition, eOpts) {
// when plugin fires "drop" event the tree fires its own "viewdrop" event
// which may be handled via ComponentQuery
me.fireEvent('viewdrop', node, data, overModel, dropPosition, eOpts);
});
},
// ...
Controller:
// controller
Ext.define('App.controller.FileManagement', {
// ...
init:function () {
this.control({
'filelist':{viewdrop:this.drop} // <-- here is selector
});
},
// etc ....