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.

Mohsen
  • 3,412
  • 3
  • 31
  • 58
  • In which method of backing bean is this code located? – partlov Jan 22 '13 at 13:48
  • updated my question: @PostConstruct method. – Mohsen Jan 22 '13 at 13:54
  • 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. – partlov Jan 22 '13 at 14:08
  • Sounds odd! preRenderView/preRenderComponent listener is not called in the included page. – Mohsen Jan 22 '13 at 14:16
  • Seems that `preRenderView` doesn't work inside an included page. See: http://stackoverflow.com/questions/12543405/prerenderview-not-firing-with-uiinclude – Mohsen Jan 22 '13 at 15:10
  • 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. – BalusC Jan 22 '13 at 15:31
  • I'm using Mojarra, and I don't care if I have direct dependency to Mojarra, since I won't switch to MyFaces. – Mohsen Jan 22 '13 at 16:10
  • Added **Why do I need this?** – Mohsen Jan 22 '13 at 16:17

3 Answers3

10

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?

Community
  • 1
  • 1
BalusC
  • 992,635
  • 352
  • 3,478
  • 3,452
  • "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? – Kenny Steegmans Dec 03 '20 at 14:56
  • Just split to a new bean if you can't guarantee. You can easily inject the "parent" bean when necessary. – BalusC Dec 03 '20 at 15:07
  • 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). – Kenny Steegmans Dec 03 '20 at 15:46
  • 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://stackoverflow.com/q/21525669 – BalusC Dec 03 '20 at 15:54
  • Sorry I meant composition not composite. (Not sure about the difference since I've never used composite) – Kenny Steegmans Dec 03 '20 at 16:42
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

Bruno F
  • 31
  • 2
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;
     }}
ravenskater
  • 484
  • 1
  • 4
  • 14