MethodExpression not firing in HtmlCommandLink - jsf

I have a dynamically generated Datatable, like this
DataTable dataTable = new DataTable();
dataTable.setValue(relatorioVOList);
dataTable.setVar("rVO");
Column checkBoxColumn = new Column();
checkBoxColumn.getChildren().add(this.viewComponentBuilder.createExpressionTextWithLink("#{rVO.iRelatorio}","#{rVO.nNome}"));
dataTable.getColumns().add(checkBoxColumn);
public HtmlForm createExpressionTextWithLink(String iRelatorioExpressionValue, String valueExpressionValue) {
HtmlForm form = new HtmlForm();
HtmlCommandLink link = new HtmlCommandLink();
//config
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
ExpressionFactory ef = application.getExpressionFactory();
ELContext elc = context.getELContext();
//value that is the reports name
ValueExpression nameValueExp = ef.createValueExpression(elc, valueExpressionValue, Object.class);
link.setValueExpression("value", nameValueExp);
//action that goes to method teste when link is clicked
MethodExpression methodExpression = createMethodExpression("#{componenteC.teste(rVO.iRelatorio)}", String.class, Integer.class);
link.setActionExpression(methodExpression);
form.getChildren().add(link);
return form;
}
private static MethodExpression createMethodExpression(String expression, Class<?> returnType, Class<?>... parameterTypes) {
FacesContext facesContext = FacesContext.getCurrentInstance();
return facesContext.getApplication().getExpressionFactory().createMethodExpression(
facesContext.getELContext(), expression, returnType, parameterTypes);
}
In ComponenteC, a RequestScopedBean, the teste function
public String teste(Integer iRelatorio) {
System.out.println("cheguei");
return "componente";
}
The goal is that the teste function will generate an url according to the iRelatorio parameter.
The issue here is that the function is never called. I tried replacing the rVO.iRelatorio with an explicit 10, "#{componenteC.teste(10)}" and even then the action seems to not be fired.
The report name is displayed correctly.

Dynamically created UIInput, UICommand and UINamingContainer components must have a fixed id assigned. Otherwise it will get an autogenerated one which is not necessarily the same when the view is restored. The component ID is used in request parameter names in submitted form data, which JSF would then use to collect the submitted input values and identify the invoked commands during apply request values phase. If the component ID changes, then JSF won't be able to perform the apply request values phase as intented.
Thus, act accordingly:
dataTable.setId("tableId");
// ...
form.setId("formId");
// ...
link.setId("linkId");
There are other potential causes, but they are not visible in the information provided so far in the question. To cover that, take your time to carefully read the following related answers on "dynamically" creating components/views:
Create inputtext dynamically
How does the 'binding' attribute work in JSF? When and how should it be used?
How to create dynamic JSF form fields
That said, you're really better off using XHTML to declare and create components instead of all that Java code mess. XHTML(+XML) is much more declarative and readable and therefore better understandable and maintainable. JSTL may be very helpful in this all.

Related

Access UI components of a page fragment in adf from a phase listener (beforePhase or afterPhase)

I'm new to Oracle ADF and I need help on getting access to the UI components of a page fragment on a JSPX file when the phase listeners of JSPX runs.
I have this .jspx file which contains an af:region inside of it. This region is connected to a page fragment (.jsff) file and I would like to access the UI components inside (e.g. inputText, etc.) of the page fragment from a page phase listener (either beforePhase or afterPhase events) of the JSPX file.
The page fragment is bound to a pageFlowScope bean and all its UI components. But when I tried to access the bean from the phase listener events, the UI components bound to the bean are not initialized yet and returns a NullPointerException.
I'm now thinking to try accessing the UI component by its "id" on the phase listener events. Is this possible?
You can retreive the value of a binded inputText or any other ADF component in your phase listener directly from the view page El Expression. Here is a simple example : https://cedricleruth.com/how-to-retreive-the-value-of-an-iterator-binding-variable-programmatically-in-adf/
In your case it will look like :
//Below is a view example with values taken from an ADF View Object. Don't forget to add an MVCE next time
//<af:inputText id="it1" autoSubmit="true" value="#{bindings.YOUR_VO.YOUR_VO_ATTRIBUTE.inputValue}" />
public static void YOUR_PHASE_EVENT(PhaseEvent pe) {
String inputTextValue= (String)this.resolveExpression("#{bindings.YOUR_VO.YOUR_VO_ATTRIBUTE.inputValue}");
//Do whatever you want with it
}
/**
* Method for taking a reference to a JSF binding expression and returning
* the matching object (or creating it).
* #param expression EL expression
* #return Managed object
* #author : Duncan Mills, Steve Muench and Ric Smith's JSFUtils class
*/
public static Object resolveExpression(String expression) {
FacesContext facesContext = getFacesContext();
Application app = facesContext.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = facesContext.getELContext();
ValueExpression valueExp = elFactory.createValueExpression(elContext, expression, Object.class);
return valueExp.getValue(elContext);
}

Dynamically created input components do not show model values

I have some fields of a bean that I need to show programmatically. Is something like this:
HtmlPanelGrid panel = new HtmlPanelGrid();
panel.setColumns(4);
HtmlOutputLabel fieldOut = new HtmlOutputLabel();
fieldOut.setId("fieldOutId");
fieldOut.setValue("name");
panel.getChildren().add(fieldOut);
HtmlInputText fieldIn = new HtmlInputText();
fieldIn.setId("fieldInId");
fieldIn.setPartialSubmit(true);
fieldIn.setValueExpression(
"field", UtilFaces.createValueExpression("#{newElementBean.fieldName}",String.class));
panel.getChildren().add(fieldIn);
mainForm.getChildren().add(panel);
In newElements.xhtml i've defined a form which is binded to mainForm, in this way:
<ice:form binding="#{newElementBean.mainForm}">
<ice:inputText id="ANOTHERFIELD" value="#{newElementBean.anotherField}"/>
<ice:commandButton action="#{newElementBean.save}" value="Save"/>
</ice:form>
When I click on the save button and I go to the next view, the field "ANOTHERFIELD" has taken the value from the bean, and shows up correctly, but the fields that were dinamically generated shows empty. Its values in the backing bean also are null. It's like the ValueExpression is not working for the HtmlInputText that I created in the backing bean. I'm using Icefaces 3.3 with Mojarra 2.1.17.
How is this caused and how can I solve it?
I solved it. I made two mistakes:
The proper setValueExpression call is like this:
fieldIn.setValueExpression("value", UtilFaces.createValueExpression("#newElementBean.fieldName}");
I was incorrectly using "field1" as 1st argument instead of "value".
This is not visble in the question, but my createValueExpression() helper method was also wrong. The following is working for me:
public static ValueExpression createValueExpression(String expression) {
Application app = FacesContext.getCurrentInstance().getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = FacesContext.getCurrentInstance().getELContext();
return elFactory.createValueExpression(elContext, expression, Object.class);
}

Refresh JSF validator attributes on rerender

I have an issue with the attributes values of a validator component.
Apparently the validator is created when I first visit a page.
Please see my code below:
<h:inputText value="#{qsetting.value}" rendered="#{qsetting.dataType=='Double'}">
<mw:validateRange min="#{qsetting.minValue}" max="#{qsetting.maxValue}" />
</h:inputText>
The inputText component is rerendered through ajax but apparently, including the value that is displayed.
Unfortunately, the qsetting.minValue and qsetting.maxValue are not refreshed, causing my validator to not work correctly.
Is there a possibility to refresh the validator, to make sure it re-retrieves its attributes or to just create a new instance of the validator?
The validator class itself is currently implementing "Validator, Serializable".
Also, I'm using jsf1.2 with facelets...
Thanks,
Steven
I've hit this problem in a non-ajax environment a few times over the years, and hit it again today. The addition of Ajax doesn't really change anything since a validator attribute is never evaluated again once the page is initially built, ajax or otherwise.
The only solution I've come up with is to set the validator attribute to a validator expression, then evaluate that expression inside the validate method.
One other issue I hit (also with JSF 1.2 and Facelets) is that not all EL variables worked. I had to use a static managed bean as the root of my expression to access the value. A facelet ui:param value as a root would not work. I haven't tested to see what else may not correctly evaluate. This could be due to another bug in the design of JSF itself. See http://myfaces.apache.org/core12/myfaces-api/apidocs/javax/faces/context/FacesContext.html#getELContext%28%29.
For example, instead of:
max="#{qsetting.maxValue}"
use
maxExpression="qsetting.maxValue"
Then
public String getMax(FacesContext context) {
Application app = context.getApplication();
ExpressionFactory exprFactory = app.getExpressionFactory();
ValueExpression ve = exprFactory.createValueExpression(context.getELContext(),
"#{" + getMaxExpression() + "}",
String.class);
Object result = ve.getValue(context.getELContext());
return (String)result;
}
public String getMaxExpression() {
return this.maxExpression;
}
public void setMaxExpression(String maxExpression) {
this.maxExpression = maxExpression;
}
//// StateHolder
public boolean isTransient() {
return isTransient;
}
public void setTransient(boolean newTransientValue) {
isTransient = newTransientValue;
}
public Object saveState(FacesContext context) {
Object[] state = new Object[1];
state[0] = maxExpression;
return state;
}
public void restoreState(FacesContext context, Object state) {
Object[] values = (Object[]) state;
maxExpression = (String) values[0];
}
UPDATE 2012-09-19:
After investigating how MyFaces Commons solves this problem, the better solution is to change the rules Facelets uses to evaluate validator and converter attribute expressions.
It basically comes down to adding a new validator or converter MetaRule which, when applied, checks to see if the attribute value is non-literal. If it is non-literal, call a special method on your validator or converter which passes in the value expression rather than the current value.
http://svn.apache.org/viewvc/myfaces/commons/trunk/myfaces-commons-validators/src/main/java/org/apache/myfaces/commons/validator/_ValidatorRule.java?view=markup
The validator at that point needs to store the value expression as state and evaluate it when needed. MyFaces commons provides all of the complicated infrastructure to make this happen generically, but you could dump all of that and write a simple custom rule and directly manage the ValueExpression yourself, similar to what I originally posted.

JSF: How to attach an actionListener to component created programatically?

I have to create some commandLinks dynamically and attach some action listener to it, So I've put <h:panelGrid> on the JSP page and used such code to add the commandLinks and to assign action listeners to:
public ManagedBean(){
List<UIComponenet> child = panelGrid.getChilderen();
list.clear();
List<MyClass> myList = getSomeList();
for (MyClass myObj : myList){
FacesContext ctx = FacesContext.getCurrentContext();
HtmlCommandLink cmdLink = (HtmlCommandLink) ctx.getApplication.createComponent(HtmlCommandLink.COMPONENT_TYPE);
cmdLink.setValue(myObj.getName());
cmdLink.setActionLinstner(new ActionListener(){
public void processAction(ActionEvent event) throws AbortProcessingException{
System.out.println (">>>>>>>>>>>>>>>>>I am HERE ");
}
});
child.add(cmdLink);
}
}
But unfortunately, when I press this commandLinks, an exception thrown! How can I add component event listeners at runtime?
(Note, the code above my contain syntax/compilation errors as I just wrote).
First, you need to manually assign ID to any dynamically created UINamingContainer, UIInput and UICommand components. Otherwise JSF can't locate them in the component tree based on the request parameters, because it wouldn't match the autogenerated ID's.
Thus, at least do:
HtmlCommandLink link = new HtmlCommandLink();
link.setId("linkId");
// ...
Second, you're supposed to create an ActionListener as MethodExpression as follows:
FacesContext context = FacesContext.getCurrentInstance();
MethodExpression methodExpression = context.getApplication().getExpressionFactory().createMethodExpression(
context.getELContext(), "#{bean.actionListener}", null, new Class[] { ActionEvent.class });
link.addActionListener(new MethodExpressionActionListener(methodExpression));
// ...
...and of course have the following method in the backing bean class behind #{bean}:
public void actionListener(ActionEvent event) {
// ...
}
All the above dynamic stuff basically does the same as the following raw JSF tag:
<h:commandLink id="linkId" actionListener="#{bean.actionListener}" />
I had the same problem.
Transient components do not work with actionListeners.
Do not call
FacesContext.getCurrentInstance().getViewRoot().setTransient(true);
or
component.setTransient(true);
As soon as I removed it, it was OK.

JSF ResponseWriter custom components

I know about startElement, endElement, and writeAttribute methods on ResponseWriter. My problem is that I want to for example output a h:commandLink by declaring it like HtmlCommandLink link = new HtmlCommandLink(); .
How can I output other UIComponents like this in my own component? I might want to use some RichFaces ajax stuff in my components aswell so hoping I can avoid making it all by scratch.
Edit: What I'm trying to do is create my own tag library with the following tag <myTags:commentTree>. Every comment have a reply button, when the reply button is clicked I render the reply form beneath the comment. Once that is rendered, I would like to output for example the richfaces <a4j:commandButton> component. This have to be done inside my own java tag file which Ive called for CommentsTreeUI.java.
Normally I output all my elements that display the forms and buttons with writer.startElement("input", myComponent); writer.writeAttribute("type", "button", null); but if I could instead do for example startElement("a4j:commandbutton", myComponent) that would help my ALOT since it has all the built in ajax features etc.
Any clues?
This problem was solved by adding new components by using
HtmlCommandButton button = new HtmlCommandButton();
button.encodeAll(context);
You can do something like this:
HtmlCommandLink link = new HtmlCommandLink();
getChildren().add(link);
It does depend on what you want to do with the child components though i.e. if you want them surrounded with custom HTML (in an HTML list, for example) you will need something a bit more complex.
One approach to making composite controls is to use the binding attribute to associate the tag with your own code:
<f:view>
<h:form>
<h:panelGroup binding="#{compositeControlBean.panelGrid}" />
</h:form>
</f:view>
The bean configuration in faces-config.xml:
<managed-bean>
<managed-bean-name>compositeControlBean</managed-bean-name>
<managed-bean-class>
composite.CompositeControlBean
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
The bean code:
/**
* Configure this bean in request scope as "compositeControlBean".
*/
public class CompositeControlBean {
private transient UIComponent panelGrid;
public UIComponent getPanelGrid() {
if (panelGrid == null) {
panelGrid = createCompositePanel();
}
return panelGrid;
}
public void setPanelGrid(UIComponent panelGrid) {
this.panelGrid = panelGrid;
}
private UIComponent createCompositePanel() {
initContextMemebers();
UIComponent commandLink = createCommandLink();
String id = view.createUniqueId();
UIComponent panelGrid = application
.createComponent("javax.faces.HtmlPanelGroup");
panelGrid.setId(id);
panelGrid.setRendererType("javax.faces.Group");
panelGrid.getChildren().add(commandLink);
return panelGrid;
}
private UIComponent createCommandLink() {
// create control
String id = view.createUniqueId();
UIComponent commandLink = application
.createComponent("javax.faces.HtmlCommandLink");
commandLink.setId(id);
commandLink.setRendererType("javax.faces.Link");
// set attributes (bind to printHello method)
Map<String, Object> attributes = commandLink
.getAttributes();
MethodExpression action = expressionFactory
.createMethodExpression(elContext,
"#{compositeControlBean.printHello}",
String.class, new Class<?>[0]);
attributes.put("value", "print hello");
attributes.put("actionExpression", action);
return commandLink;
}
private transient FacesContext context;
private transient Application application;
private transient ELContext elContext;
private transient ExpressionFactory expressionFactory;
private transient UIViewRoot view;
private void initContextMemebers() {
context = FacesContext.getCurrentInstance();
application = context.getApplication();
elContext = context.getELContext();
expressionFactory = application.getExpressionFactory();
view = context.getViewRoot();
}
public String printHello() {
System.out.println("Hello");
return null;
}
}

Resources