Tuesday, March 2, 2010

Clearing Properties Set on the Client in ADF Faces

Oracle Adf Faces is based on JSF, which has a server-side component model. One of the things ADF Faces adds is a component model on the client. This means you can get/set component attributes on the client using javascript.

When you set a value on the client, that value is actually sent to the server and set on the component, so that when it is rerendered the new value will be shown. Sometimes this isn't the behavior a user is expecting. This blog will suggest some coding workarounds.

Let's look at an example. An ADF Faces inputText component has a 'changed' attribute. When the 'changed' attribute is true, a blue circle is shown to the left of the label, indicating that the value has changed. Here's what an input looks like when 'changed' is set to true:



So let's say I want to show the changed indicator as soon as the user changes the value. Then when I save the page I re-render, the changed indicator should not be shown since the value has now been saved. You might start with a page like the one below. In this code we are listening for a valueChangeEvent on the client, and when we get it we set 'changed' to true using javascript.

<?xml version='1.0' encoding='utf-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich" version="1.2">
<jsp:directive.page contentType="text/html;charset=utf-8"/>
<f:view>
<af:document id="d1" inlineStyle="padding:20px">
<af:form>
<af:resource type="javascript">
function changed(event) {
var inputComponent = event.getSource();
inputComponent.setChanged(true);
}
</af:resource>
<af:inputText id="it" label="label">
<af:clientListener method="changed" type="valueChange"/>
</af:inputText>
<af:commandButton text="Submit"/>
</af:form>
</af:document>
</f:view>
</jsp:root>



If you run this page in Jdev 11.1.1.2.0 you'll find that you can enter a value in the input, tab out, and the changed indicator does in fact show up. So we're done, right? Well not quite, because you'll also find that when you hit submit the changed indicator does not disappear like we want it to.

Why is the value of changed saved? Under the covers client side convenience setters like setChanged() are really calling AdfUIComponent.setProperty. The doc for setProperty says "Changing the value of a property supported by the component will normally result in the new property value being synchronized with the server automatically." In other words when you call setProperty on the client, the new property value will be sent to the server, and the new value is saved on the component. This means the component will rerender based on the new attribute value. In our example this means that when we submit and the component is rerendered it still thinks changed is true, and thus the blue changed icon is still visible.

When is the new value sent to the server? The new value will be sent to the server the next time there's traffic to the server. In some cases that happens right away, in other cases not.

So what follows are some coding alternatives to consider. Note that depending on how your app is written, some of these may work better than others if validation fails on the server.

Coding Alternative 1



The first coding alternative moves the logic to the server. In this example:
  • in the input tag bind changed to a request scoped value
  • in the input tag set autoSubmit to true
  • in the valueChangeListener for the input component
    • set the request scoped value that the 'changed' attribute is bound to to true
    • call context.renderResponse
    • set the input component as a partial target so that the input component rerenders itself.




<?xml version='1.0' encoding='utf-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich" version="1.2">
<jsp:directive.page contentType="text/html;charset=utf-8"/>
<f:view>
<af:document id="d1" inlineStyle="padding:20px">
<af:form>
<af:inputText label="label"
autoSubmit="true"
changed="#{test.changed}"
valueChangeListener="#{test.valueChange}"/>
<af:commandButton text="Submit" />
</af:form>
</af:document>
</f:view>
</jsp:root>



And the test backing bean code, which is request scope looks like:



import javax.faces.event.ValueChangeEvent;
import oracle.adf.view.rich.context.AdfFacesContext;

public class TestBean {
public TestBean() {}

public void valueChange(ValueChangeEvent valueChangeEvent)
{
setChanged(true);
AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance();
adfFacesContext.addPartialTarget(valueChangeEvent.getComponent());
FacesContext.getCurrentInstance().renderResponse();
}

public void setChanged(boolean changed)
{
_changed = changed;
}

public boolean isChanged()
{
return _changed;
}
private boolean _changed;
}



Coding Alternative 2


Coding alternative 1 relies on having autoSubmit and a valueChangeListener. In this particular example that works, but in other examples you may not have an event that is going to the server. In those cases you might want to use a serverListener, here's the example:


<?xml version='1.0' encoding='utf-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich" version="1.2">
<jsp:directive.page contentType="text/html;charset=utf-8"/>
<f:view>
<af:document id="d1" inlineStyle="padding:20px">
<af:form>
<af:resource type="javascript">
function changed(event)
{
var inputComponent = event.getSource();
AdfCustomEvent.queue(inputComponent, "myCustomEvent", null, true);
}
</af:resource>
<af:inputText label="label" changed="#{test2.changed}">
<af:serverListener type="myCustomEvent"
method="#{test2.doCustomEvent}"/>
<af:clientListener method="changed" type="valueChange"/>
</af:inputText>
<af:commandButton text="Submit"/>
</af:form>
</af:document>
</f:view>
</jsp:root>



And the test backing bean code, which is request scope looks like:


package test;

import javax.faces.context.FacesContext;

import oracle.adf.view.rich.context.AdfFacesContext;
import oracle.adf.view.rich.render.ClientEvent;

public class Test2Bean
{
public Test2Bean()
{
}

public void doCustomEvent(ClientEvent event)
{
setChanged(true);
AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance();
adfFacesContext.addPartialTarget(event.getComponent());
FacesContext.getCurrentInstance().renderResponse();
}

public void setChanged(boolean changed)
{
_changed = changed;
}

public boolean isChanged()
{
return _changed;
}
private boolean _changed;

}




Coding Alternative 3


Another option is to call setChanged on the client, which will cause the changed attribute to be set to true on the server, but then we will set changed back to false in the submit button's action listener. Here's the code:


<?xml version='1.0' encoding='utf-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich" version="1.2">
<jsp:directive.page contentType="text/html;charset=utf-8"/>
<f:view>
<af:document id="d1" inlineStyle="padding:20px">
<af:form>
<af:resource type="javascript">
function changed(event) {
var inputComponent = event.getSource();
inputComponent.setChanged(true);
}
</af:resource>
<af:inputText binding="#{test3.input}" label="label">
<af:clientListener method="changed" type="valueChange"/>
</af:inputText>
<af:commandButton text="Submit" actionListener="#{test3.clear}"/>
</af:form>
</af:document>
</f:view>
</jsp:root>




And the test backing bean code, which is request scope looks like:

package test;

import javax.faces.event.ActionEvent;

import oracle.adf.view.rich.component.rich.input.RichInputText;

public class Test3Bean
{
public Test3Bean()
{
}

public void clear(ActionEvent actionEvent)
{
_input.setChanged(false);
}

public void setInput(RichInputText input)
{
_input = input;
}

public RichInputText getInput()
{
return _input;
}

private RichInputText _input;
}

14 comments:

  1. this is very helpful blog .it solve many confusion ..
    and you can find the helpful blog on these links

    http://goo.gl/2csMwk
    http://goo.gl/o9XcoJ
    http://goo.gl/0GUCMN
    http://goo.gl/7V4g2V

    ReplyDelete

  2. Google provides you relationship between your blog to another person blog see more and get the best relationship
    Relation.1
    Relation.2
    Relation.3
    Relation.4
    Relation.5


    ReplyDelete
  3. A very informative site. The way you have share this information is really very appreciative. Hope to see more on this topic here. Our idea of Property Management starts in assisting our clients in identifying
    2 bed Apartment Lagos

    ReplyDelete
  4. Its very good and very excellent article. i really enjoyed this post and i hope you will keep posting this kinds of post in future. Mortgages Portugal has established itself as a leading Algarve Mortgages in the Portuguese market that offers professional mortgage advice for resident and non-resident property buyers

    ReplyDelete
  5. No bank homes that you can buy without a bank loan up front. Buy a home now, and get your loan later. and no any bank homes loans independence for me.its very good information in your blog and its helpful for us .

    ReplyDelete
  6. Very Good paper! 10/10 I want to thank the writer and the staff support for the work. Thanks a lot.Home living furniture reviews Home living Stores contemporary range of furniture beds & home style. Best prices on textiles

    ReplyDelete
  7. Nice article. Think so new form of features have included in your article. Waiting for your next article.Mortgage broker in Mississauga We know how to present your transaction to smartly capture a lender’s attention and deliver the best available loan structure and interest rate.

    ReplyDelete
  8. Good work on the blog as it is helping very many people out there. I don’t think there is anything such as the best simple writing service. I think different writers work well for different individuals depending on their needs and the topics that they are working on.
    KLCC Penthouse

    ReplyDelete
  9. I think you are right. I was pleased when I heard one of my student saying, "I like it here because you are not a bunch of eggheads." We definitely hire tutors who can develop a positive rapport with the student.

    Emergency plumber Los Angeles

    ReplyDelete
  10. I truly favor to reading your post. Thank you a lot for taking the time for you to share such a nice info.

    _____________________
    Ideal mortgages in Portugal

    ReplyDelete
  11. Very efficiently written information. It will be valuable to everyone who uses it, including myself. Thanks a lot. Storage Heater Repairs This are very nice service repair so i am very impressed .

    _________________________
    Storage Heater Repairs

    ReplyDelete
  12. I want to thank you for posting such an educational blog. And also will wait for your next post.

    --------------
    Sell Land

    ReplyDelete
  13. If some one wants to be updated with newest technologies then he must be pay a visit this site and be up to date every day. Home Insurance Lakewood Ranch

    ReplyDelete
  14. I really like and appreciate your blog post, thanks again. Want some more stuff.
    My Room Rent

    ReplyDelete