How to retrieve value of a ui:param in the backing bean
Asked Answered
M

3

5

I'm passing a parameter p1 to another page page.xhtml:

<ui:include src="page.xhtml">
    <ui:param name="p1" value="#{someObject}"/>
</ui:include>

Is this possible to evaluate #{p1} inside @PostConstruct method of the backing bean of page.xhtml? Using the following piece of code, #{p1} cannot resolve:

FacesContext currentInstance = FacesContext.getCurrentInstance();
currentInstance.getApplication().evaluateExpressionGet(currentInstance, "#{p1}", String.class);

Why do I need this?

I'm using an xhtml file (say component.xhtml) as a custom UI component. This file has a backing bean from which I should get component data. Since I'm including this xhtml file twice or more in my main JSF page, I want to pass different objects to each of component.xhtml so that my component work with my custom data each time included.

Microtome answered 22/1, 2013 at 13:36 Comment(8)
In which method of backing bean is this code located?Thayne
updated my question: @PostConstruct method.Microtome
That can be a problem. @PostConstruct method can be executed before page is constructed. Try to define f:event for preRenderView and see if it is resolved.Thayne
Sounds odd! preRenderView/preRenderComponent listener is not called in the included page.Microtome
Seems that preRenderView doesn't work inside an included page. See: #12543905Microtome
Are you using Mojarra or MyFaces? This construct fails definitely in MyFaces. In Mojarra there's a way, but you'd rather not depend on JSF impl specific behavior. If you elaborate the concrete functional requirement, we may be able to propose the right solution.Gwendolyn
I'm using Mojarra, and I don't care if I have direct dependency to Mojarra, since I won't switch to MyFaces.Microtome
Added Why do I need this?Microtome
G
11

In Mojarra, you can get it as an attribute of the FaceletContext. You can get it in the @PostConstruct of a managed bean which is guaranteed to be referenced/constructed for the first time in the included page (and thus not in the parent page before the <ui:param> is declared in the component tree).

FaceletContext faceletContext = (FaceletContext) FacesContext.getCurrentInstance().getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
Object p1 = faceletContext.getAttribute("p1");

In MyFaces, the whole FaceletContext isn't available in managed beans as it's discarded by end of view build time and this construct would then not work. To be JSF implementation independent, you might want to consider to set it via <c:set scope="request"> instead. It's then available as a request attribute.

As to the concrete functional requirement, consider creating a comoposite component with a backing component. For some examples, see our composite component wiki page and this blog about using multiple input components in a composite component. See also When to use <ui:include>, tag files, composite components and/or custom components?

Gwendolyn answered 22/1, 2013 at 16:33 Comment(5)
"a managed bean which is guaranteed to be referenced/constructed for the first time in the included page" If the page is loaded at the same time as the parent page, it should still be ok as long as it was never used in the parent right? Or do we need to lazy load it to be sure of that?Healy
Just split to a new bean if you can't guarantee. You can easily inject the "parent" bean when necessary.Gwendolyn
Well I have a bean specifically for my composite but my param is not added to the context even when I lazy load the composite (using primefaces overlay).Healy
Composite? That's not an "included page". Use a backing component instead. See also the links in bottom of answer. Then you can simply do something like https://mcmap.net/q/18186/-initialize-a-composite-component-based-on-the-provided-attributesGwendolyn
Sorry I meant composition not composite. (Not sure about the difference since I've never used composite)Healy
A
2

The param is not available in the @PostConstruct method; you can use the preRenderComponent event to initialize the parameters inside your backing bean; just put it after the ui:composition of the included page, it will be executed before the rendering of the included page itself.

Following the OP example of a passing a p1 parameter to a page.xhtml template

the main page:

<ui:include src="page.xhtml">
    <ui:param name="p1" value="#{someObject}"/>
</ui:include>

page.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    ...>

<ui:composition>
    <f:event listener="#{backingBean.init(p1)}" type="preRenderComponent"/>
        ...

</ui:composition>

</html>

BackingBean.java:

@ViewScoped
 public class BackingBean{

     private Object p1;
     public void init(Object value){        
     this.p1=p1;
    }

    ...
 }

the event is fired before the render of the ui:composition tag, that is before the render of page.xhtml

Autopilot answered 17/10, 2018 at 10:36 Comment(0)
E
0

This works for me:

<ui:include src="page.xhtml">
     <ui:param name="p1" value="#{someObject}"/>
</ui:include>

page.xhtml:

<c:set var="data" value="#{p1}" scope="request"/>

Your bean:

@ViewScoped
public class ManagedBean{

     private Object someObject;

     public Object getSomeObject(){
         if(someObject== null){
            HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
            someObject= request.getAttribute("data");
          }
          return someObject;
     }

     public void setSomeObject(Object someObject){
          this.someObject = someObject;
     }}
Epigone answered 3/5, 2017 at 15:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.