2 Replies Latest reply on Jan 7, 2010 6:23 PM by Florian Petersen

    Dynamic Variable Available in EL?

    Oguzhan YALCIN Newbie

      Hi,
      I have a component(facelet) fro buttons viewed on a page. This components renders the buttons on a page according to the permissions. On every module this components displays buttons like Save,Edit,Delete...


      The buttons.xhtml is below:




      <f:view 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:richfaces="http://richfaces.ajax4jsf.org/rich"
           xmlns:ajax="https://ajax4jsf.dev.java.net/ajax"
           xmlns:a4j="http://richfaces.org/a4j"
           xmlns:s="http://jboss.com/products/seam/taglib"
           xmlns:c="http://java.sun.com/jstl/core"
           xmlns:rich="http://richfaces.org/rich">
      
                <rich:panel>
                     <div id="MainContainer">
                     <div id="LeftContainer">
                     <s:button id="BackButton"  action="#{HomeEntity.update}" value="Back" />
                      </div>
                     <div id="CenterContainer">
                     <s:button id="UpdateButton"  action="#{HomeEntity.update}" value="Update" rendered="#{HomeEntity.managed}"  disabled="#{!HomeEntity.wired}"/> 
                     <s:button id="SaveButton"  action="#{HomeEntity.persist}" value="Save"  rendered="#{!HomeEntity.managed}" disabled="#{!HomeEntity.wired}" /> 
                     <s:button id="AddButton"  action="#{HomeEntity.update}" value="New" /> 
                     <s:button id="ViewButton"  action="#{HomeEntity.update}" value="View" /> 
                     <s:button id="EditButton"  action="#{HomeEntity.update}" value="Edit" /> 
                     <s:button id="DeleteButton"  action="#{#{HomeEntity}.update}" value="Delete" /> 
                     <s:button id="PrintButton"  action="#{HomeEntity.update}" value="Print" /> 
                     </div>
                     <div id="RightContainer">
                          <s:button id="ForwardButton"  action="#{HomeEntity.update}" value="Forward" /> 
                     </div>
                     <br clear="both" />
                     </div>
                </rich:panel>
      </f:view>





      I call the component from other components like:


                


      <ui:include src="/UC/common/Buttons.xhtml" >
          <ui:param name="HomeEntity" value="#{registryHome}" />
      </ui:include>




      If the form uses healthHome class I want to pass healtHome class and use it on the component with the name HomeEntity. for example I pass registryHome to HomeEntity variable as displayed above . registryHome has several methods. I want to call registryHome.persist by simply typing HomeEntity.persist to h:commandbuttons action property as shown in the buttons.xhtml. Is stg like #{#{EntityHome}.persist} available with EL?
      Thanks

        • 1. Re: Dynamic Variable Available in EL?
          Florian Petersen Newbie
          Hi Oguzhan,

          did you find a solution for this? I also try to do something like:
          <s:decorate template="textbox.xhtml">
            <ui:param name="value" value="user.lastname"/>
            ...
          </s:decorate>

          and then, in textbox.xhtml:
          <h:inputText id="tbox" value="#{#{value}}">

          I finally had the idea to create a proxy to get what i want, just like:
          <h:inputText id="tbox" elExp="#{value}" value="#{bindingProxy.expLangVar}">

          The idea behind this is that the expLangVar getters and setters read the elExp attribute of the calling component and evaluate it. But of course it would be necessary to know the calling component (tbox) here... Could be possibly accomplished by iterating the call stack using reflection i guess, but that would surely be unelegant and time consuming... The class:

          @Name("bindingProxy")
          public class BindingProxy {

            public String getExpLangVar() {
              UIComponent component = ???;
              String elExpression = (String)component.getAttributes().get("elExp");
              Object elVarVal = Expressions.instance().createValueExpression(elExpression).getValue();
              return (String)elVarVal;
            }

            public void setExpLangVar(String value) {
              UIComponent component = ???;
              String elExpression = (String)component.getAttributes().get("elExp");
              Expressions.instance().createValueExpression(elExpression).setValue(value);
            }
          }


          My first approach to this was to use a ValueChangeListener implementation:
          <h:inputText id="tbox" elExp="#{value}" value="#{bindingProxy.expLangVar}" valueChangeListener="#{bindingProxy.processValueChange}">

          For the listener, it's no problem to access the input element and thus the elExp attribute value via the event parameter:
          @Name("bindingProxy")
          public class BindingProxy {
            private Map<String, Object> elExpressions;

            public String getExpLangVar() {
              String callerId = ???
              String elExpression = (String)elExpressions.get(callerId);
              Object elVarVal = Expressions.instance().createValueExpression(elExpression).getValue();
              return (String)elVarVal;
            }

            public void setExpLangVar(String value) {
              String callerId = ???
              String elExpression = (String)elExpressions.get(callerId);
              Expressions.instance().createValueExpression(elExpression).setValue(value);
            }

            public void processValueChange(ValueChangeEvent event) throws AbortProcessingException {
              UIComponent component = (UIComponent) event.getSource();
              String elExpression = (String)component.getAttributes().get("elExp");
              elExpressions.put(component.getId(), elExpression);
            }
          }

          But the problem here is to make this value accessible for the expLangVar getter and setter. I discovered that i definitely need to determine the caller id in the getter/setter :-/
          I tried an approach using an Interceptor here, but without success. As i said, i probably could fetch it via call stack, but i want to avoid this like hell...

          OR MAYBE YOU HAVE A TOTALLY DIFFERENT APPROACH?
          Any help would be greatly appreciated...

          Thanx a lot,
          best regards,
          Flo
          • 2. Re: Dynamic Variable Available in EL?
            Florian Petersen Newbie
            Hello again,
            just wanted to inform you that i found a solution. Maybe it was way too simple ;-)
            I created some kind of factory as seam component, so every UI component could register its own BindingProxy:

            <h:inputText id="#{id.trim()}" elExp="#{value}" value="#{bindingProxyFactory.getProxy(value).expLangVar}">

            The factory class checks if a proxy has already been created for the given el expression. If so, it is returned, if not, it is created and registered. Pretty straightforward i think:

            @Name("bindingProxyFactory")
            public class BindingProxyFactory {
              private Map<String, BindingProxy> proxies;

              public BindingProxyFactory() {
                proxies = new HashMap<String, BindingProxy>();
              }

              public BindingProxy getProxy(String expression) {
                BindingProxy proxy = proxies.get(expression);
                if (proxy == null) {
                  proxy = new BindingProxy(expression);
                  proxies.put(expression, proxy);
                }
                return proxy;
              }

            }

            And last but not least the proxy with getter/setter as only really interesting part:

            public class BindingProxy {
              private String elExpression = "";

              public BindingProxy(String expression) {
                this.elExpression = expression;
              }

              public String getExpLangVar() {
                Object elVarVal = Expressions.instance().createValueExpression("#{" + elExpression + "}").getValue();
                return (elVarVal != null ? elVarVal.toString() : null);
              }

              public void setExpLangVar(String value) {
                try {
                  ValueExpression<Object> valueExp = Expressions.instance().createValueExpression("#{" + elExpression + "}");
                  Object converted = convert(valueExp.getType().getSimpleName(), value);
                  valueExp.setValue(converted);
                }
                catch(Exception e) {
                  //...
                }
              }

              private Object convert(String type, String val) throws Exception {
                Object ret = null;
                if (type.equalsIgnoreCase("Date")) {
                  DateFormat df = new SimpleDateFormat("dd.MM.yyyy");
                  ret = df.parse(val);
                }
                else if (type.equalsIgnoreCase("Integer"))
                  ret = Integer.parseInt(val);
                else if (type.equalsIgnoreCase("Long"))
                  ret = Long.parseLong(val);
                else if (type.equalsIgnoreCase("Double"))
                  ret = Double.parseDouble(val);
                else
                  ret = val;
                return ret;
              }

            }

            So maybe this helps out someone else, and if there is still a more elegant way (maybe out-of-the-box) please let me know...
            Take care, best regards
            Flo