2

I might not be thinking correctly in terms of visual components in JSF, but I guess that's part of my question. My question is around the seeming lack of scope around variables declared within JSF <ui:component> implementations.

So, say I have /resources/comp/myPanel.xhtml:

<?xml version="1.0" encoding="UTF-8" ?>
<ui:component xmlns="http://www.w3.org/1999/xhtml"
              xmlns:h="http://java.sun.com/jsf/html"
              xmlns:f="http://java.sun.com/jsf/core" 
              xmlns:ui="http://java.sun.com/jsf/facelets"
              xmlns:c="http://java.sun.com/jsp/jstl/core"      
              xmlns:cc="http://java.sun.com/jsf/composite">
    <cc:interface>
    </cc:interface>
    <cc:implementation>
        <f:loadBundle var="bundle" basename="panelOnly.bundle" />

        <h:outputText value="#{bundle.myText}" />
    </cc:implementation>
</ui:component>

And there is a resource bundle that gets loaded in that component, panelOnly/bundle.properties:

myText = This is a panel resource

And then I have a page that places the myPanel component, mainPage.xhtml:

<?xml version="1.0" encoding="UTF-8" ?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core" 
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:comp="http://java.sun.com/jsf/composite/comp">
    <h:body>
        <f:view>
            <f:loadBundle basename="mainPage.bundle" var="bundle" />

            <comp:myPanel />

            <h:outputText value="#{bundle.myText}" />
        </f:view>
    </h:body>
</html>

and there is a resource bundle that gets loaded in the main page, mainPage/bundle.properties:

myText = This is a main page resource

Now, I would assume that my page should render as:

This is a panel resource
This is a main page resource

But, instead, I get:

This is a panel resource
This is a panel resource

And I assume that is because I clobbered what the "bundle" symbol refers to in my component so that when the mainPage.xhtml tries to resolve that value, it looks to the component's "bundle" object and not the original mainPage's.

My workaround to date has been to just use unique named variables within my components that would never clash with variables on my main pages. But I would prefer if there was a way to coax JSF to recognize anything declared in my component as locally scoped variables and not clobber the caller's symbols.

I think there are and other tags that one can use to make locally scoped variables under #{cc.attrs...}. If you could enumerate my local scoping options in your answer, that would be very helpful. I suspect my <f:loadBundle> is a special case, and maybe there isn't a workaround for that one as it was not designed with <ui:component> in mind.

Thanks!

P.S. I'm running Mojarra 2.1.1 (FCS 20110408)

(edited for formatting and copy and paste bugs 6/15/2011)

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
DWoldrich
  • 3,817
  • 1
  • 21
  • 19

1 Answers1

3

Unfortunately, that's how <f:loadBundle> works. It's an one-time setting for the entire view. And any subsequent <f:loadBundle> calls in the same view will just override the previous one.

Your best bet is to manage it by a backing component.

<cc:interface componentType="myPanel">

with

@FacesComponent(value="myPanel")
public class MyPanel extends UIComponentBase implements NamingContainer {

    private ResourceBundle bundle;

    public MyPanel() {
        bundle = ResourceBundle.getBundle("panelOnly.bundle", 
            FacesContext.getCurrentInstance().getViewRoot().getLocale());
    }

    @Override
    public String getFamily() {
        return "javax.faces.NamingContainer";
    }

    public ResourceBundle getBundle() {
        return bundle;
    }

}

which can be used as

<cc:implementation>
    <h:outputText value="#{cc.bundle.myText}" />
</cc:implementation>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Wow, I'm always impressed by your mastery of JSF! So many great answers like this one on StackExchange. I will experiment with FacesComponent. I see by your usage example that I can use NamingContainer to hang additional symbols off the cc object through getters. I suppose I can pass initializer parameters to my NamingContainer through the ``'s? – DWoldrich Jun 15 '11 at 20:28
  • 1
    It's not exactly that. The backing `UIComponent` of a composite component is **required** to implement `NamingContainer` and return that as family. See also the description of `componentType` attribute of the tag documentation: http://download.oracle.com/javaee/6/javaserverfaces/2.0/docs/pdldocs/facelets/composite/interface.html The `NamingContainer` just takes care of generating the proper client IDs. Thanks to `componentType` the `#{cc}` now refers to an instance of the `MyPanel` class which you can extend with more methods. You can just access its getters and actions the "usual" EL way. – BalusC Jun 15 '11 at 20:33
  • 1
    For another example, see also my answer on this question http://stackoverflow.com/questions/6358066 – BalusC Jun 15 '11 at 20:36
  • What you say makes sense. I have the oracle javadocs link you refer to in my bookmarks, but I do not truly understand JSF's architecture until I read discussions like this one. To date, I've always thought of `` as letting one chunk up content - but states and actions would be reserved for separate view/session/request scoped beans. I see now from your examples here that through componentType, I can add state and behaviors to my ``. Very instructive, thanks again! – DWoldrich Jun 15 '11 at 20:50