icefaces: remove with effect

So you have your JSF+Icefaces application that displays some list of things, and you want the user to be able to remove items from the list. Add in a small twist: the removed item should make use of the Icefaces Effect SDK and fade out the removed item.

Sounds good so far. Add the effect attribute to your component,

<ice:panelGroup
  effect="#{bean.effect}"
  ...

Now go to your remove action (listener) method and add the effect initialization to your bean,

public void removeListener(ActionEvent e) {
  beanList.remove(bean);
  Effect e = new Fade();
  e.setSubmit(true);
  e.setTransitory(false);
  bean.setEffect(e);

When you run this code, you won't see the effect. A little thought makes the problem obvious. When the bean is removed from the list, the view is updated immediately. Since the bean is no longer in the list, it is not rendered, at all, with any effect. How do we get our remove effect then?

There's actually two solutions. The first is to simply not remove the item from the list, and keep a “visible” flag indicating if the item is really in the list. The most obvious way to do this is to change removeListener() above to,

public void removeListener(ActionEvent e) {
  beanList.setVisible(false);
  Effect e = new Fade();
  e.setSubmit(true);
  e.setTransitory(false);
  bean.setEffect(e);

and change your view,

<ice:panelGroup
  effect="#{bean.effect}"
  visible=”#{bean.visible}”
...


However, when you run this code, you get the same result. The item disappears immediately and you do not see the effect. It's the same problem. The visible flag is set immediately and the component is hidden in the view immediately. The trick here is an undocumented aspect of the Effect SDK. If the effected component has a visible attribute, and it's bound to a value, the effect will set the value to true or false depending on the type of effect. For example, a Fade sets it to false, and Appear sets it to true. Keeping this in mind, we remove the setVisisble(false) call from the removeListener() method and we're done.

This is not a great solution though, as it requires you to check the visible flag wherever you consider the list of items in your business logic. The second solution involves creating a phase listener that really removes the item from the list, after the effect completes. It goes like this. First create a bean to represent phase event,

public class PhaseEventAction {

public class PhaseEventAction {
  private PhaseId phaseId;
  private boolean doBeforePhase;
  private String action;
  private Object[] arguments;
  private Class[] parameters;

 

Now create an implementation of PhaseListener to process the above phase events,

public class QueuedActionListener implements PhaseListener {
  public PhaseId getPhaseId() {
    return PhaseId.ANY_PHASE;
  }

  public void beforePhase(PhaseEvent pe) {
    checkForOperations(true, pe);
  }
  public void afterPhase(PhaseEvent pe) {
    checkForOperations(false, pe);
  }
  private void checkForOperations(boolean doBeforePhase, PhaseEvent evt) {
    FacesContext fc = FacesContext.getCurrentInstance();
    QueuedActionBean qab = (QueuedActionBean)fc.getApplication().
      createValueBinding("#{queuedActionBean}").getValue(fc);
    List<PhaseEventAction> invoked = new ArrayList<PhaseEventAction>();

    for (PhaseEventAction pea : qab.getPhaseEventActions()) {
      if (pea.getPhaseId() == evt.getPhaseId()) {
        if (pea.isDoBeforePhase() == doBeforePhase) {
          javax.faces.application.Application a = fc.getApplication();
          MethodBinding mb = a.createMethodBinding(pea.getAction(), pea.getParameters());
          if (mb != null) {
            mb.invoke(fc, pea.getArguments());
            invoked.add(pea);
          }
        }
      }
    }

    qab.getPhaseEventActions().removeAll(invoked);
  }
}  

The idea is that we're creating a queue of events. Specifically in this case, clients will add remove events to the queue to be processed not until the RENDER_PHASE. Let's keep going. Create a bean to hold the queue,

public class QueuedActionBean implements Serializable {
  private List<PhaseEventAction> phaseEventActions = 
    new ArrayList<PhaseEventAction>();
  public List<PhaseEventAction> getPhaseEventActions() {
    return phaseEventActions;
  }
}

Now in faces-config.xml, declare the queue bean,

<managed-bean>
  <managed-bean-name>queuedActionBean</managed-bean-name>
  <managed-bean-class>com.sun.identity.admin.model.QueuedActionBean</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

and declare the phase listener,

<lifecycle>
  <phase-listener>
    com.sun.identity.admin.model.QueuedActionListener
  </phase-listener>
</lifecycle>

Client beans that want to play this game inject queuedActionBean into themselves,

<managed-property>
  <property-name>queuedActionBean</property-name>
    <value>#{queuedActionBean}</value>
  </managed-property>
</managed-bean>

and add PhaseEventAction objects onto the queue. JSF calls QueuedActionListener, which processes the events if the conditions are right. In this case, we want the phase ID to be RENDER_RESPONSE and doBeforePhase to be false. Here's our new removeListener() that includes the code to add the PhaseEventAction to the queue,

public void removeListener(ActionEvent e) {
  Effect e = new Fade();
  e.setSubmit(true);
  e.setTransitory(false);
  bean.setEffect(e);
  PhaseEventAction pea = new PhaseEventAction(); 
  pea.setDoBeforePhase(false);  
  pea.setPhaseId(PhaseId.RENDER_RESPONSE);
  pea.setAction("#{viewConditionsHandler.handleRemove}");
  pea.setParameters(new Class[] { SomeClass.class });
  pea.setArguments(new Object[] { someObject });

  getQueuedActionBean().getPhaseEventActions().add(pea);
}

While we used this to solve a very specific problem, the solution is generalized. Set into the PhaseEventAction the phase, whether to do the action before or after the phase, the action method, the signature of the action method, and the arguments to pass to the action method.

Comments:

Hi

I want to do something similar, I want to show an effect before navigating away from a page. Could this solution be applied to that scenario ?

Posted by xworker on April 23, 2009 at 01:13 AM PDT #

the QueuedActionListener doesn't have the power to initiate an action, and therefore navigation. it seems like you'd need to start a JSF navigation from the PhasedEventAction's action method. i don't know how to do that or if it's even possible.

another option would be to re-structure you app some. instead of two pages with navigation between them, use one page with two virtual pages that are hidden or show in a mutually exclusive manner. then the PhasedEventAction's action method sets one section visible and hides the other.

good luck.

Posted by Jeffrey Blattman on April 23, 2009 at 01:53 AM PDT #

Alain commented on my previous post that the solution there was problematic as it allowed access to arbitrary view which may depend on objects being set up in other views

Posted by links of london on December 13, 2009 at 02:02 PM PST #

jean style jean style jeans stylejeans style mens jean style mens jean style skinny jean style skinny jean style blue jeans style blue jeans style seven jeans style seven jeans style new jean style new jean style latest jean style latest jean style jeans styles jeans styles boy style jeans boy style jeans men jean style men jean style jean style 

Posted by jean style on December 17, 2009 at 02:19 PM PST #

The select items have object values. http://www.watchgy.com/ All is good. When I submit the form, I see "Validation Error: Value is not valid". My first reaction was that I didn't have any validation on the page, so how could a value be not valid? http://www.watchgy.com/tag-heuer-c-24.html
http://www.watchgy.com/rolex-submariner-c-8.html

Posted by replica rolex on December 28, 2009 at 11:59 PM PST #

"The trick here is an undocumented aspect of the Effect SDK. If the effected component has a visible attribute, and it's bound to a value, the effect will set the value to true or false depending on the type of effect."

Very good tip, thanks a lot! The ICEfaces documentation is really bad. I would like ICEfaces more, if the docs and tutorials would be better. However, thanks man.

- Tim

Posted by Tim Büthe on August 03, 2010 at 08:01 PM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

jtb

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today