Focus event

Hello all.
I'm pretty new to echo2, but have inherited an app this uses it, and am trying to figure out how to set an action listener to fire off an event when a text component gains or loses focus. If anyone can post a code snippet I would really appreciate it.

Thanks, T

jadeblade's picture

Focus Event? Me Too

I too would like to have an event for when a component gains the focus. But even more, I would like to have an event when a component looses the focus. I think it would be quite useful to do data validity checking as the user moves away from the textbox (or whatever).

J

You will need to modify the

You will need to modify the existing text field component...I have to say its a little complicated so I suggest that you read some articles / tutorials about creating your own copponent.

These are the steps you may follow -
You will need to create a new text field component by copy paste code. So call it MyTextField, MyTextFieldPeer, MyTextField.js etc. Its bit tricky so copy paste all the code from corresponding components.. don't worry about inheriting some features from super class just now.

Now you need the caller to add the focus lost listener to your text field. Provide two methods in your text field class add/remove focusLostListener

public interface MyTextComponentIF {
   public static final String CONNECT_ON_BLUR = "connectOnBlur";
   public static final String PROPERTY_FOCUS_LOST_COMMAND = "focusLostCommand";
}

public class MyTextField extends TextField {
   // All other code

    private void setConnectOnBlur(boolean connect) {
        setProperty(MyTextComponentIF.CONNECT_ON_BLUR, connect);
    }
    public void addFocusLostListener(FocusLostListener l) {
        this.setConnectOnBlur(true);
        getEventListenerList().addListener(FocusLostListener.class, l);
        firePropertyChange(ACTION_LISTENERS_CHANGED_PROPERTY, null, l);
    }
	
     public void removeFocusLostListener(FocusLostListener l) {
        getEventListenerList().removeListener(FocusLostListener.class, l);
        firePropertyChange(ACTION_LISTENERS_CHANGED_PROPERTY, null, l);
        EventListener[] listeners = getEventListenerList().getListeners(FocusLostListener.class);
        if(listeners.length == 0) {
             // Remember you are changing this property but this is not sent to the client so even if there is 
// no focusLostlistener client will still try to send the message. You need to support partial update to 
// change this property. OR remove and add the text component. More on partial updates later...you 
// can see TextUpdate class in TextComponentPeer
             this.setConnectOnBlur(false);
        }
     }
}

public interface FocusLostListener extends EventListener, Serializable {
	    public void focusLost(ActionEvent e);
}

Now you need to inform this to the client side component.., which is done via TextComponentPeer

public class MyTextComponentPeer implements ActionProcessor, ComponentSynchronizePeer, DomUpdateSupport, FocusSupport, ImageRenderSupport, PropertyUpdateProcessor {
    // All other code

    public void renderInitDirective(RenderContext rc, TextComponent textComponent) {
       // All other code

       Object connectOnBlur = textComponent.getRenderProperty  (MyTextComponentIF.CONNECT_ON_BLUR);
      if (connectOnBlur != null) {
	itemElement.setAttribute(MyTextComponentIF.CONNECT_ON_BLUR, String.valueOf((Boolean) connectOnBlur));
      } else {
	itemElement.setAttribute(MyTextComponentIF.CONNECT_ON_BLUR, "false");
      }
   }
}

You also need to specify this mapping of component and component peer in SynchronizePeerBindings.properties

Now this will be propagated to the client...need to modify javascript file... MyTextComponent.js

    processInit: function(initMessageElement) {
        for (var item = initMessageElement.firstChild; item; item = item.nextSibling) {
            var elementId = item.getAttribute("eid");
            var textComponent = new MyTextComponent(elementId);

            // All other

            textComponent.connectOnBlur = item.getAttribute("connectOnBlur");  

            // All other
    }      

// Now init()

init: function() {
      var element = this.getElement();
            
       // All other
      
      element.connectOnBlur = this.connectOnBlur;
            
       // All other
}

// Now modify processBlur

processBlur: function(echoEvent) {
        var connectOnBlur = this.connectOnBlur;
        if (!this.enabled || !EchoClientEngine.verifyInput(this.getElement())) {
            return;
        }
        
        this.updateClientMessage();
        if (connectOnBlur == "true") {
                // "focusLost" value should be same as MyTextComponentIF.PROPERTY_FOCUS_LOST
        	EchoClientMessage.setPropertyValue(this.elementId, "focusLost", "true");
        	EchoServerTransaction.connect();        
        }
        EchoFocusManager.setFocusedState(this.elementId, false);
    }

Now on focus lost this text component will connect to the server. Need to get the message on the server. For any component which can send some action like button etc. framework calls a method called processInput(actionName, actionValue)

This method is already there in TextComponent.java

So lets override it our textfield class

public class MyTextField extends TextField {

      // All other code

               @Override
	public void processInput(String inputName, Object inputValue) {
		super.processInput(inputName, inputValue);
		if (MyTextComponentIF.PROPERTY_FOCUS_LOST.equals(inputName)) {
			fireFocusLostEvent();
		}
	}

	private void fireFocusLostEvent() {
		if (!hasEventListenerList()) {
			return;
		}
		EventListener[] listeners = getEventListenerList().getListeners(FocusLostListener.class);
		ActionEvent e = null;
		for (int i = 0; i < listeners.length; ++i) {
			if (e == null) {
				e = new ActionEvent(this, (String) getRenderProperty(MyTextComponentIF.PROPERTY_FOCUS_LOST_COMMAND));
			}
			((FocusLostListener) listeners[i]).focusLost(e);
		}
	}

}

That's it, your focusLost() in the listener should be called.

Regards,
Pawan