How do I attach an event handler to a panel in extJS?

37,489

Solution 1

[For anyone else reading this, I went through a fairly thorough explanation of this already here.]

You are missing a few key concepts:

click is not a valid Panel event, so adding a click handler on a Panel will do nothing (this is the issue in most of the code you posted above).

In this code:

var menuItem1 = new Ext.Panel({
   ...
}); 
content.body

You copy-pasted from another answer, but incorrectly. content in the other code referenced the Panel that was created -- it is a variable. In this code you created the Panel as menuItem1 but then are trying to reference it as content?

Please re-read my previous explanation about how rendering works in the other answer I gave you. You must either add a Panel to a container that renders it, or render it directly (via the renderTo config). If you do neither, the Panel will not show up.

Using jQuery's delegate function is not the proper approach with Ext JS components.

Solution 2

try this:

var menuItem1 = new Ext.Panel({
    id: 'panelStart',
    title: 'Start',
    html: 'This is the start page.',
    cls:'menuItem',
    listeners: {
        afterrender: function (comp) {
            var element = comp.getEl();
            element.on('click', function() {
                alert('ok')
            });
        }
    }
}); 

Solution 3

If you want to listen to events on Ext.Elements inside of a panel, you use the element property when calling addListener or passing in listeners config, instead of waiting for the afterrender event just to set the listeners

var menuItem1 = new Ext.Panel({
    id: 'panelStart',
    title: 'Start',
    html: 'This is the start page.',
    cls:'menuItem',
    listeners: {
        click: {
           element: 'el', // could be 'body', or any other Ext.Elements 
                          // that are available from the component
           fn: function() {}
        }
    }
}); 
Share:
37,489
Angry Dan
Author by

Angry Dan

web/software developer, .NET, C#, WPF, PHP, software trainer, English teacher, have philosophy degree, love languages, run marathons my tweets: http://www.twitter.com/edward_tanguay my runs: http://www.tanguay.info/run my code: http://www.tanguay.info/web my publications: PHP 5.3 training video (8 hours, video2brain) my projects: http://www.tanguay.info

Updated on July 09, 2022

Comments

  • Angry Dan
    Angry Dan almost 2 years

    All I want to do is handle a click on an extJS panel.

    I've tried all the of suggestions on this question plus all I could find elsewhere but each of them fail in the ways described below.

    What is the correct syntax to add a click event handler to a extJS panel?

    [Edit: For the sake of others who may find this question later, I'll add some comments inline to make this easier to decipher --@bmoeskau]

    doesn't execute handler:

    var menuItem1 = new Ext.Panel({
        id: 'panelStart',
        title: 'Start',
        html: 'This is the start page.',
        cls:'menuItem',
        listeners: {
            click: function() {
                alert('ok');
            }
        }
    }); 
    

    [Ed: click is not a Panel event]

    doesn't execute handler:

    var menuItem1 = new Ext.Panel({
        id: 'panelStart',
        title: 'Start',
        html: 'This is the start page.',
        cls:'menuItem',
        listeners: {
            render: function(c) {
                c.body.on('click', function() {
                    alert('ok');
                });
            }
        }
    }); 
    

    [Ed: The Panel is never being rendered -- add renderTo config. Next, you'll hit a null error telling you that c is not a valid variable. Do you mean menuItem1 instead?]

    doesn't execute handler:

    var menuItem1 = new Ext.Panel({
        id: 'panelStart',
        title: 'Start',
        html: 'This is the start page.',
        cls:'menuItem'
    }); 
    Ext.getCmp('panelStart').on('click', function() {
        alert('ok');
    });
    

    [Ed: click is not a Panel event. Also, the Panel is not yet rendered, so if you switched the callback to be on the element rather than the Component you'd get a null error.]

    gets error: Ext.get('panelStart') is null:

    var menuItem1 = new Ext.Panel({
        id: 'panelStart',
        title: 'Start',
        html: 'This is the start page.',
        cls:'menuItem'
    }); 
    Ext.get('panelStart').on('click', function() {
        alert('ok');
    });
    

    [Ed: It's not rendered, see above. Switching from getCmp to get means you are switching from referencing the Component (which does exist, but does not have a click event) to referencing the Element (which does have a click event, but is not yet rendered/valid).]

    makes the panel disappear:

    var menuItem1 = new Ext.Panel({
        id: 'panelStart',
        title: 'Start',
        html: 'This is the start page.',
        cls:'menuItem',
        listeners: {
            'render': {
                fn: function() {
                    this.body.on('click', this.handleClick, this);
                },
                scope: content,
                single: true
            }
        },
        handleClick: function(e, t){
            alert('ok');
        }        
    }); 
    

    [Ed: The scope being passed into the callback (content) is not a valid ref in this code (this was copy-pasted incorrectly from another sample). Since the Panel var is created as menuItem1 and the callback is intended to run in the panel's scope, scope var should be menuItem1. Also, this Panel is never rendered, see prev comments.]

    gives the error "Ext.fly(menuItem1.id) is null":

    var menuItem1 = new Ext.Panel({
        id: 'panelStart',
        title: 'Start',
        html: 'This is the start page.',
        cls:'menuItem'
    }); 
    Ext.fly(menuItem1.id).addListener('click', Ext.getCmp(menuItem1.id) , this);
    

    [Ed: Panel is not rendered, see above]

    ...put outside Ext.onReady()... gets error: Ext.getCmp('panelStart') is null

    Ext.getCmp('panelStart').on('click', function() {
        alert('okoutside');
    });
    

    [Ed: Panel is likely not rendered at the time this code is run. Also, click is not a Panel event.]

    ...put outside Ext.onReady()... gets error: Ext.get('panelStart') is null

    Ext.get('panelStart').on('click', function() {
        alert('okoutside');
    });
    

    [Ed: See above]

    ...put outside Ext.onReady()... gets error: Ext.fly('panelStart') is null

    Ext.fly('panelStart').on('click', function() {
        alert('okoutside');
    });
    

    [Ed: See above]

    For the last three, I checked in Firebug and <div id="startPanel"> exists:

    alt text

    It works with JQuery:

    So with JQuery I simply have to do this and it works:

    $('body').delegate(('#panelStart'), 'click', function(){
        alert('ok with jquery');
    });
    

    [Ed: This is not a good approach. It's simply delaying attaching of the handler until later, when the element actually shows up in the DOM (which could also be done via Ext btw, but would still not be the proper approach). I spelled out the correct approach, as linked in my answer below. The OP's attempts are all very close, but each is missing one or two key pieces.]

    How can I attach a click handler like this with extJS?

  • Brian Moeskau
    Brian Moeskau over 13 years
    This code will do nothing until the Panel is actually rendered, either via renderTo or by adding it to a container that renders it. Also, the afterrender code is not really a very good approach to solving this problem.
  • Gerrat
    Gerrat over 13 years
    Well, I assumed this wasn't the complete code - only a snippet. I also think it's pretty obvious that this code won't do anything until after it's rendered...the 'afterrender' listener kind of implies that. This approach does work, but I'll certainly accept that maybe it's not a "good approach" (was just the first thing that came to mind). Perhaps you could add a comment here on why this isn't a good approach. Thanks.
  • Brian Moeskau
    Brian Moeskau over 13 years
    I clarified about rendering because the OP obviously does not understand this. Some of his null errors are obviously due to the Panel never being rendered, and in the code he posted in his original questions, he was attaching listeners to Panel's internal elements prior to adding the panel into a container that renders it. This code will work OK, but a better approach would be to assign the handler as a separate function on the Panel rather than an anonymous fn. continued to next comment...
  • Brian Moeskau
    Brian Moeskau over 13 years
    Also, rather than handling clicks on a Panel's el directly, it's usually preferable to target a specific child, like the body. The Panel already has internal click handling, e.g. in the header bar, so you may run into issues handling clicks on the underlying el (and also you normally would not want to respond to clicks in the header/footer in app code). Most likely the OP actually wants something like myPanel.body.on('click', ...) where myPanel has already been rendered. Note that this could be in the render/afterrender callback too, as you show.
  • Gerrat
    Gerrat over 13 years
    Oh sure...go for the whole "teach a man to fish" approach! :)
  • Angry Dan
    Angry Dan over 13 years
    great, thanks for all the comments, I'll work through your comments to get a solution to work in my code so I can replace the jquery approach. It makes me wonder though: why the Panel is so difficult to attach a click event to, I have been able to get quickly up to speed with handlers on buttons and grid rows with "handler..." and "listeners..." but the panel seems to be such a special case.
  • Brian Moeskau
    Brian Moeskau over 13 years
    The Panel is not a special case -- it's just that the 'click' event does not make sense for the entire Panel, as a Component (it makes perfect sense for a button, grid row, or other Components). As such, it is not exposed via the Panel API. DOM events are always available on DOM nodes, but they are only made available via the Component event API when it makes sense to do so. In your case myPanel.body.on('click', ...); is the correct approach, just make sure the panel is rendered before doing it.
  • Angry Dan
    Angry Dan over 13 years
    this makes sense now, I was getting a menuItem1.header is undefined error but only when I would include menuItem1 in an accordion layout, the trick is to put the menuItem1.header.on('click',...) AFTER the definition of the accordion layout since only then does the menuItem1 exist, thanks for clarifying this.
  • Ruan Mendes
    Ruan Mendes over 11 years
    @bmoeskau The new easy way to do it is listeners: {click:{element: 'body, fn: function(){}'}}
  • Ruan Mendes
    Ruan Mendes over 11 years
    @Gerrat This works, but there's a new simpler way to do it, see my answer