This discussion is archived
6 Replies Latest reply: Aug 7, 2008 9:24 AM by 601554 RSS

Add clientListener dynamically?

601554 Newbie
Currently Being Moderated
I would like to add a clientListener to input components via a phaseListener before the page is rendered. I've attempted this to no avail and now I'm wondering if it's feasible. If anyone could point me in the right direction, I would appreciate it.

Thanks,

Matt
  • 1. Re: Add clientListener dynamically?
    Frank Nimphius Employee ACE
    Currently Being Moderated
    Hi,

    I once wrote an interesting blog entry about this

    http://www.oracle.com/technology/products/jdev/tips/fnimphius/clientListener/client-server-listener.html

    See "Configuring a client and server listener at designtime"

    Frank
  • 2. Re: Add clientListener dynamically?
    601554 Newbie
    Currently Being Moderated
    Hi Frank,

    Thanks for the reply... I would actually like to add a clientListener to every input component in my application at run time without binding. I've written the code to iterate the view and add the listner to the input components (which works fine), but I just need a hook to execute it prior to the first render. I've tried a phaseListener to no avail and now I'm looking into a custom viewHandler. Any suggestions?

    Thanks,

    Matt
  • 3. Re: Add clientListener dynamically?
    487442 Expert
    Currently Being Moderated
    Hi Matt,

    I didn't test what I'm going to propose, but it should work, in theory that is.

    1. Create an Application class implementing decorator pattern
    public class MyApplication extends Application
    {
        private Application wrapped;
    
        public MyApplicationFactory(Application wrapped)
        {
            this.wrapped = wrapped;
        }
    
        private UIComponent addClientListeners(UIComponent component)
        {
            if (component instanceof UIXInput)
            {
                // It's an input, add the listener
    
                // Since there's no interface for ClientListener purpose, we have to use reflection
                try
                {
                    Method getClientListeners = component.getClass().getMethod("getClientListeners", null);
    
                    ClientListenerSet cls = (ClientListenerSet)getClientListeners.invoke(component, null);
                    cls.addListener("event", "clientMethodCall");
                }
                catch (NoSuchMethodException e)
                {
                    // The new input component doesn't support ClientListener, either log, no-op or crash
                }
            }
    
            return component;
        }
    
        // Override all methods, simply calling the wrapped.method version, except for the following:
    
        public UIComponent createComponent(String componentType)
        {
            return addClientListeners(wrapped.createComponent(componentType));
        }
    
        public UIComponent createComponent(ValueBinding componentBinding, FacesContext context, String componentType)
        {
            return addClientListeners(wrapped.createComponent(componentBinding, context, componentType));
        }
    
        public UIComponent createComponent(ValueExpression componentExpression, FacesContext context, String componentType)
        {
            return addClientListeners(wrapped.createComponent(componentExpression, context, componentType));
        }
    }
    2. Create an ApplicationFactory class implementing decorator pattern
    public class MyApplicationFactory extends ApplicationFactory
    {
        private ApplicationFactory wrapped;
        private boolean decorated;
    
        public MyApplicationFactory(ApplicationFactory wrapped)
        {
            this.wrapped = wrapped;
            this.decorated = false;
        }
    
        public Application getApplication()
        {
            Application application = wrapped.getApplication();
            if (!decorated)
            {
                application = new MyApplication(application);
                setApplication(application);
                decorated = true;
            }
    
            return application;
        }
        
        public void setApplication(Application application)
        {
            wrapped.setApplication(application);
        }
    }
    3. Register the application factory in your faces-config.xml


    Note that it's a pretty advanced JSF strategy, but has the benefit to show the powerful modularization mechanism used by the technology. Also note that it will fail if the application doesn't use JSF correctly, that is by directly instantiating components rather than using Application as the sole UIComponent factory. Therefore, as a best practice (actually the only one always working), always use the Application class to instantiate your UIComponents. It's going to be annoying at first, but you're going to be very happy of having done so in the long run, I assure you.


    Regards,

    ~ Simon
  • 4. Re: Add clientListener dynamically?
    601554 Newbie
    Currently Being Moderated
    Hi Simon,

    Thank you for that strategy - it worked out perfectly and will definitely come in handy down the road!

    Best Regards,

    Matt
  • 5. Re: Add clientListener dynamically?
    Frank Nimphius Employee ACE
    Currently Being Moderated
    Matt,

    what is the usecase for this ? As a recommendation of best practices you should avoid coding in JavaScript for as long as you could (better performance). Also note that currently the application wont run if a clientListener is applied, pointing to a non existing JS function

    Frank
  • 6. Re: Add clientListener dynamically?
    601554 Newbie
    Currently Being Moderated
    Frank,

    Here's a simplified usecase: Our application has numerous forms with a lot of data. On value change of any field the styling on the tab label must change to italic to indicate the form has not been saved and must also flip a flag in the managed been that indicates there has been a change. On close of a tab the client must recieve a "You have data that has not been saved, would you like to close the tab without saving?" dialog if the flag indicates there was a change.

    I could accomplish this by adding a valueChangeListener to each component, but I wanted to avoid calling the server on value cange on every field. Instead I implimented a clientListener that will only poke the server on the first field change.

    Regards,

    Matt