using a variable value as an a href link - jsf

I have a backing bean and an XHTML that is tied to this backing bean. I have a private instance variable called testerString.
Here is an excerpt of the backingbean
public class LinkDataBackingBean {
Map<String, LinkInfo> linkMap = new HashMap<String, LinkInfo>();
List<LinkInfo> list = new ArrayList<LinkInfo>();
private String testerString = "http://www.google.com";
public String getTesterString() {
return testerString;
}
public LinkDataBackingBean() throws FileNotFoundException {
System.out.println("hello");
for(int i = 0; i < 10; i++){
list.add(new LinkInfo("hi", "www.google.com"));
//linkMap = getLinkData()
}
}
As you can see, I've manually populated the variable with http://www.google.com. There comes a time in my XHTML where I would like to have someone click on a link and then it will go to the URL described by testerString.
One way I tried this was this, as a passed variable.
<f:metadata>
<f:viewParam name="id" value="#{linkDataBackingBean.testerString}" />
</f:metadata>
And then:
TEST
This did not work, in the sense that "TEST" did not appear as a hyperlink at all (whereas if I manually put in "http://www.google.com" for the value of the former statement, it DOES appear as a hyperlink and works fine).
I've also tried the following, plugging the backing bean value directly (have to screw up formatting or SO won't show):
TEST
This yielded a similar result - TEST did not appear as an underlined hyperlink kind of thing.
I considered using outputLink, but faced similar woes, I do get the Text to be a hyperlink, but it does not link to the domain I specified:
<h:outputLink id="link1" value="#{ linkDataBackingBean.testerString}">
<h:outputText value="TEST" />
</h:outputLink>
This links to the base dir of my whole project - http://localhost:8080/name-of-my-proj/faces/
When I do the same as above, but populate value with "http://www.google.com", it properly goes there. It doesn't seem to get/understand my reference to the backingbean.
What sticking me is identifying what would be required to have TEST appear as a hyperlink and, when someone clicks on it, them being directed to the URL indicated by testerString? Surely there is some JSF "plumbing" I am missing.

Related

How many ELResolver[s] is/are associated with an ELContext in a JSF application?

Here it is mentioned that
Though only a single ELResolver is associated with an ELContext, there
are usually multiple resolvers considered for any given variable or
property resolution.
For the sake of making it understandable to the one going to answer, I am demonstrating it taking into consideration a custom ELResolver. This is only for illustration purposes. I haven't felt the need for a home-brewed custom ELResolver ever in the projects in which I have worked so far.
Inside the CustomELResolver, getValue() method looks like,
#Override
public Object getValue(ELContext ctx, Object base, Object property)
throws NullPointerException, PropertyNotFoundException, ELException {
logger.log(Level.INFO, "Get Value property : {0}", property);
if ((base == null) && property.equals(SOME_PROPERTY)) {
logger.log(Level.INFO, "Found request {0}", base);
ctx.setPropertyResolved(true);
return SOME_OBJECT;
}
return null;
}
Each value expression is evaluated behind the scenes by the getValue
method. Adding this in faces-config.xml, the custom
resolver is added in the chain of responsibility.
a simple facelet page:
<h:outputText value="#{bean.name}" />
<br />
<br />
<b>Ordered:</b>
<br />
<h:dataTable id="tableId1"
value="#{PROPERTY DECLARED IN CUSTOM RESOLVER CLASS}" var="t">
<h:column>#{t}</h:column>
</h:dataTable>
with
#ManagedBean(name = "bean")
#RequestScoped
public class Bean {
private String name = "Rafael";
// getters & setters
}
When I debug, for this expression value="#{PROPERTY DECLARED IN CUSTOM RESOLVER CLASS}" from the above page, the call delegated to the getValue() in CompositeELResolver, where the CustomELResolver highlighted in red is considered.
Whereas, for this expression
value="#{bean.name}"
a normal ManagedBeanELResolver is considered. Absolutely, no issues with that.
But for the same request, the ELContext was clearly associated with 2 ELResolvers.
Please elucidate as to what the documentation meant in the first paragraph as mentioned above
...a single ELResolver is associated with an ELContext...
You forgot to read the next sentence in the link you refer to.
ELResolvers are combined together using CompositeELResolvers, to
define rich semantics for evaluating an expression.
If you look in the call hierarchy, you'll see 1 (one, a single) FacesCompositeELResolver... So there is one CompositeResolver directly associated with the ELContext.
But you could also read it that effectively only one EL resolver is actually doing the work each time, the one in the 'chain' that says "I've resolved it, here is the result"

Illegal Syntax for Set Operation: How to tell JSF I don't "want" a setter

This question may be more of the type "conceptual" or "I don't understand JSF".
My scenario:
I have a JSF Page (index.xhtml) where I use a p:accordionPanel (but I don't think it matters what component it is). What I want to do is to set the activeIndexes of it.
<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever')}">
// bla bla...
</p:accordionPanel>
And the (simplified) method in the backing bean:
public String getActiveIndexesForSections(String holderName){
String activeSections = "";
for(Section s : sectionMap.get(holderName)){
if (s.isActive())
//add to the string
}
return activeSections;
}
Now this works just fine on a normal page load.
But if I click on a p:commandButton (with ajax=false) (or anything else which "sends" data back to the server I guess) - I get the following exception:
/WEB-INF/tags/normalTextSection.xhtml #8,112 activeIndex="#{myController.getActiveIndexesForSections(name)}": Illegal Syntax for Set Operation
// bla..
Caused by: javax.el.PropertyNotWritableException: Illegal Syntax for Set Operation
After some googling / reading the error message I found that I need a setter.
First of all: I don't want a setter - do I really need one or is there a way to tell JSF I don't want this "behavior".
Second I realized that it's not that "easy" to provide a setter, because my method has a parameter (so public void setActiveIndexesForSections(String name, String activeIndexes) or public void setActiveIndexesForSections(String name)won't work).
What I came up with in the end is:
Create a (generic) "Pseudo-Property-class":
// just a dummy class since the class is recreated at every request
public class Property<T> implements Serializable {
private T val;
public Property(T val) {
this.val= val;
}
public T getVal() {
return val;
}
//no need to do anyhting
public void setVal(T val) {
}
}
Change the bean method:
public Property<String> getActiveIndexesForSections(String holderName){
String activeSections = "";
for(Section s : sectionMap.get(holderName)){
if (s.isActive())
//add to the string
}
return new Property<String>(activeSections);
}
And call it from the index.xhtml:
<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever').val}">
// bla bla...
</p:accordionPanel>
This works but obviously is a ugly hack/workaround.
What is the proper way to handle a situation like this? Or is what I'm doing simply completely wrong?
The setter is needed to remember the active indexes as they were when the form is submitted. Basically, you need to bind it as a value expression (with a property), not as a method expression (like an action method), nor to an unmodifiable collection (like activeIndex="#{param.tab}"). Exactly like as with input values. Technically, you're indeed doing it "simply completely wrong" ;)
The requirement is however understood. Given that you're really not interested in the changed active indexes, and thus want to reset them to defaults on every form submit, then you can bypass it by storing the result as a request attribute with help of <c:set>. This way you will fool EL to set it in the request attribute map instead of the intented bean property.
<c:set var="activeIndex" value="#{myController.getActiveIndexesForSections('whatever')}" scope="request" />
<p:accordionPanel multiple="true" activeIndex="#{activeIndex}">
<!-- bla bla... -->
</p:accordionPanel>
Under the covers, it will basically do externalContext.getRequestMap().put("activeIndex", value) as setter operation, which will obviously just work.
Update: upon inspecting the source code of AccordionPanel component, I saw another workaround given the fact that the activeIndex won't be set when the rendered attribute evaluates false. So just alter the rendered attribute to behave exactly that: evaluate false during update model values phase (the 4th phase).
<p:accordionPanel multiple="true"
activeIndex="#{myController.getActiveIndexesForSections('whatever')}"
rendered="#{facesContext.currentPhaseId.ordinal ne 4}">
<!-- bla bla... -->
</p:accordionPanel>

adding duplicate components to a JSF panel grid

I am reasonably new to Java/JSF development (previously Mainframe legacy) so I am hoping someone will be able to let me know if what I am about to ask is possible and how to do it. Or at least point me in the right direction.
I'm trying to create a portlet where I can add the same component over and over again up to a maximum of 30 times. Eventually the data behind these components will be different in each one but from the same database. What I've done so far is create a container component and a button which calls a method to add something to the container. Like as follows:
<h:body>
<h:form>
<ice:panelGrid binding="#{simpleBean.containerComponent}"/>
<br/>
<ice:commandButton value="add new section" action="#{simpleBean.addComponent}" />
</h:form>
</h:body>
What I'm struggling with is the backing bean to create and render the component. At the minute I am just trying to create anything that repeats. The final code will be more complex than this.
private HtmlPanelGrid containerComponent;
private int i = 1;
public void addComponent() {
UIColumn uiColumn = new UIColumn();
uiColumn.setId("UIColumn_"+i);
HtmlOutputText htmlOutputText = new HtmlOutputText();
htmlOutputText.setId("HtmlOutputText_"+i);
htmlOutputText.setValue("HtmlOutputText_1_Value");
HtmlInputText htmlInputText = new HtmlInputText();
htmlInputText.setId("HtmlInputText_"+i);
htmlInputText.setValue("HtmlInputText_1_Value");
uiColumn.getChildren().add(htmlOutputText);
uiColumn.getChildren().add(htmlInputText);
if (containerComponent == null) {
containerComponent = new HtmlPanelGrid();
}
containerComponent.getChildren().add(uiColumn);
i++;
}
I can get the code to render something the first time the button is clicked but nothing happens subsequently. In an ideal world I'd get 30 of the same component displayed on the screen.
Can anyone offer any advice?
Thank you in advance.
Chris

JSF SelectItems and escaping (xss)

there is a selectOneMenu in my example with a f:selectItems-attribute. The select-items are resolved from my bean like this:
<h:selectOneMenu value="#{bean.value}">
<f:selectItems value="#{bean.selectItems}" var="obj" itemValue="#{obj}" itemLabel="#{obj.name}"/>
</h:selectOneMenu>
The method getSelectItems() in my bean looks like that:
public List<MyObject> getSelectItems() {
List<MyObject> list = new LinkedList<MyObject>();
MyObject obj = new MyObject("Peter");
list.add(obj);
return list;
}
The objects that are displayed are simple objects with a attribute "name".
Nothing special up to this point. But now i change my method to that:
public List<MyObject> getSelectItems() {
List<MyObject> list = new LinkedList<MyObject>();
MyObject obj = new MyObject("<script>alert('xss is bad');</script>");
list.add(obj);
return list;
}
The javascript doesn´t get escaped by MenuRenderer-Class and my page shows me the alert-message.
Is there any cause why the default value of the escape-attribute of SelectItem is "false"?
How can i fix that problem? (I use Mojarra 2.1.7)
The default should indeed not have been false. I've reported it as issue 2747.
In the meanwhile, add itemLabelEscaped="true" to escape it anyway.
<f:selectItems ... itemLabelEscaped="true" />
Note that this is only necessary when you're using GenericObjectSelectItems, i.e. when you're supplying a E[]/List<E>/Map<K, V> instead of List<SelectItem>/SelectItem[]. Also note that escaping is only absolutely mandatory when it concerns user-controlled input (which is fortunately very rarely the case in dropdown values).

JSF: How to bind many of h:selectBooleanCheckbox?

I have a problem to bind list of h:selectBooleanCheckbox to my bean.
Anybody helps ?
This is not working:
<ui:repeat value="#{cartBean.productsList}" var="cartProduct" varStatus="i">
<h:selectBooleanCheckbox binding="#{cartBean.checkboxes[i.index]}" />
</ui:repeat>
public class CartBean extends BaseBean {
public List<Product> getProductsList() {...}
private HtmlSelectBooleanCheckbox[] checkboxes;
public HtmlSelectBooleanCheckbox[] getCheckboxes() {
return checkboxes;
}
public void setCheckboxes(HtmlSelectBooleanCheckbox[] checkboxes) {
this.checkboxes = checkboxes;
}
}
I get error:
javax.faces.FacesException: javax.el.PropertyNotFoundException: /WEB-INF/flows/main/cart.xhtml #26,97 binding="#{cartBean.checkboxes[i.index]}": Target Unreachable, 'checkboxes' returned null
I solved my problem. I used code like below and get what i want (thanks to BalusC blog - http://balusc.blogspot.com/2006/06/using-datatables.html#SelectMultipleRows):
<ui:repeat value="#{cartBean.productsList}" var="cartProduct" varStatus="i">
<h:selectBooleanCheckbox value="#{cartBean.selectedIds[cartProduct.id]}" />
</ui:repeat>
public class CartBean extends BaseBean {
private Map<Integer, Boolean> selectedIds = new HashMap<Integer, Boolean>();
public Map<Integer, Boolean> getSelectedIds() {
return selectedIds;
}
}
Your concrete problem is caused because the binding attribute is evaluated during view build time, that moment when the XHTML source code is turned into a JSF UI component tree, while the <ui:repeat> runs during view render time, that moment when the JSF UI component tree needs to produce HTML.
In other words, the #{i.index} is only available during view render time and evaluates as null during view build time. In effects, you're doing a binding="#{cartBean.checkboxes[null]}"
There's another conceptual mistake here: you seem to expect that the <ui:repeat> produces physically multiple <h:selectBooleanCheckbox> components. This is untrue. There's physically only one <h:selectBooleanCheckbox> which is reused multiple times to produce HTML based on the currently iterated variable. Actually, binding="#{cartBean.checkbox}" was been sufficient. However, collecting the values is a story apart. I won't go in detail, but you can find several hints in this answer: Validate order of items inside ui:repeat.
In order to achieve the (apparent) concrete functional requirement of generating physically multiple <h:selectBooleanCheckbox> components and binding each to a separate array item, you should be using an iteration component which runs during view build time instead of view render time. That's the JSTL <c:forEach>:
<c:forEach items="#{cartBean.productsList}" var="cartProduct" varStatus="i">
<h:selectBooleanCheckbox binding="#{cartBean.checkboxes[i.index]}" />
</c:forEach>
But, after all, using binding on a bean property should be avoided as much as possible. Use instead exactly that attribute which you ultimately need: the value attribute. This way you don't need to do a HtmlSelectBooleanCheckbox#getValue() everytime. You already figured the right solution with a Map<Integer, Boolean> selectedIds:
<ui:repeat value="#{cartBean.productsList}" var="cartProduct">
<h:selectBooleanCheckbox value="#{cartBean.selectedIds[cartProduct.id]}" />
</ui:repeat>
See also:
JSTL in JSF2 Facelets... makes sense?
I don't know if you can bind elements stored in an array. But in your code, the problem is that your HtmlSelectBooleanCheckbox[] is null. So maybe change your Java code to:
public HtmlSelectBooleanCheckbox[] getCheckboxes() {
if (checkboxes == null) {
checkboxes = new HtmlSelectBooleanCheckbox[getProductsList().size()];
}
return checkboxes;
}
but I am really not sure if it will work... Maybe the solution is to not bind your HtmlSelectBooleanCheckbox elements in the Java code. Why do you need to bind them?

Resources