Note that there are some explanatory texts on larger screens.

plurals
  1. POJSF : Variable not accessible in ValueExpression : Wrong FaceletContext and no access to UIComponent’s VariableMapper
    primarykey
    data
    text
    <p>I have an application with complex validations which I can only perform based on the model hierarchy, so I can not use JSF’s validation stage.</p> <p>My problem is that I need to link back the validation errors to the gui (visually show the textfields that need attention). So I need to link UI Components with domain model items somewhere.</p> <p>I was hoping to realise this using a PreRenderComponentEventListener : </p> <ul> <li>my validatorcomponent stores Constraintvalidations where I have access to the object and property that needs attention. </li> <li>I use the ValueExpression of the UIComponent to retrieve the correct object the Component binds to.</li> </ul> <p>At first this seemed to work : I have the following in a xhtml page :</p> <pre><code>&lt;ui:composition template="/templates/mainTemplate.xhtml"&gt; &lt;ui:define name="content"&gt; &lt;ui:repeat value="#{contactController.contactManager.contactList}" var="contact"&gt; &lt;h:inputText value="#{contact.name}"/&gt; &lt;h:inputText value="#{contact.firstName}"/&gt; &lt;br/&gt; &lt;/ui:repeat&gt; &lt;/ui:define&gt; &lt;/ui:composition&gt; </code></pre> <p>Now assume I have a list of contacts where one firstName fails.</p> <p>ValueExpression’s expressionstring gives me “#{contact.firstName}” : I build a new ValueExpression from this one to retrieve the parent “#{contact}” and can check if the failing firstname is the one of the correct contact in the list.</p> <p>So I do something like this in the Listener :</p> <pre><code>private Object extractParent(UIInput input) { FaceletContext faceletElContext = (FaceletContext) FacesContext.getCurrentInstance().getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY); ValueExpression orig = input.getValueExpression("value"); String parent = getParent(orig.getExpressionString()); ValueExpression valueExpression = FacesContext.getCurrentInstance().getApplication().getExpressionFactory().createValueExpression(faceletElContext,"#{"+parent+"}", Object.class); return valueExpression.getValue(faceletElContext); } </code></pre> <p>Now this only works when the repeat is in the top facelet. <br/>As soon As my complex app uses include files and or this start to fail this because of 2 reasons :</p> <ul> <li>The facelet context is the wrong one, not the one of the nested xhtml and thus it does not have the variable in it’s variableMapper</li> <li>The variable is defined in the UIComponent’s valueexpression itself but that one is not accessible, has private access only so I can not use that one either.</li> </ul> <p>So I am completely stuck ! Can someone provide me an alternative please ? </p> <p><strong>UPDATE after evaluation of response from @BalusC</strong> : I tried with the <code>ELContext</code> but without success. I investigated a bit further and the core problem is that neither <code>ELContext</code> nor <code>FaceletContext</code> allows me access to variables defined in include files : </p> <ul> <li>the <code>ELContext</code> does not have them</li> <li>the <code>FaceletContext</code> seems to be the one of the last include file all the time and thus not the correct one.</li> </ul> <p>I have a small testcase which proves this : When rendering the "contactinner" inputtext the ELContext has not this variable and the faceletcontext has the wrong xhtml hierarchy.</p> <p>All help very very much appreciated. </p> <p>test.xhtml :</p> <pre><code>&lt;ui:composition&gt; &lt;h:form id="myform"&gt; &lt;c:set var="itemContact" value="#{contactController.contact}"/&gt; &lt;c:set var="itemPerson" value="#{contactController.person}"/&gt; &lt;ui:include src="contact.xhtml"/&gt; &lt;ui:include src="person.xhtml"/&gt; &lt;h:commandButton action="#{contactController.process}"/&gt; &lt;/h:form&gt; &lt;/ui:composition&gt; </code></pre> <p>contact.xhtml :</p> <pre><code>&lt;ui:composition&gt; &lt;c:set var="contactinner" value="#{itemContact}"/&gt; &lt;h:inputText value="#{contactinner}"/&gt; &lt;/ui:composition&gt; </code></pre> <p>person.xhtml :</p> <pre><code>&lt;ui:composition&gt; &lt;c:set var="personinner" value="#{itemPerson}"/&gt; &lt;h:inputText value="#{personinner}"/&gt; &lt;/ui:composition&gt; </code></pre> <p>faces-config.xml (extract) :</p> <pre><code>&lt;system-event-listener&gt; &lt;system-event-listener-class&gt;TestSystemEventListener&lt;/system-event-listener-class&gt; &lt;system-event-class&gt;javax.faces.event.PreRenderComponentEvent&lt;/system-event-class&gt; &lt;/system-event-listener&gt; </code></pre> <p>TestSystemEventListener (extract) :</p> <pre><code>public class TestSystemEventListener implements SystemEventListener { @Override public void processEvent(SystemEvent event) throws AbortProcessingException { UIInput input = (UIInput) event.getSource(); FaceletContext faceletElContext = (FaceletContext) FacesContext.getCurrentInstance().getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY); FacesContext context = FacesContext.getCurrentInstance(); ValueExpression orig = input.getValueExpression("value"); } </code></pre> <p>Running this, when the evenlistener is launched for the inputext in the first xhtml <code>#{contactInner}</code> I get teh following data in the listener :</p> <pre><code>orig = {com.sun.faces.facelets.el.TagValueExpression@24201}"/xhtml/contact.xhtml @6,43 value=\"#{contactinner}\"" orig = {com.sun.el.ValueExpressionImpl@24220}"ValueExpression[#{contactinner}]" expr = {java.lang.String@24270}"#{contactinner}" varMapper = {com.sun.el.lang.VariableMapperImpl@24271} vars = {java.util.HashMap@24275} size = 1 [0] = {java.util.HashMap$Entry@24278}"contactinner" -&gt; "/xhtml/contact.xhtml @5,54 value=\"#{itemContact}\"" context = {com.sun.faces.context.FacesContextImpl@24200} elContext = {com.sun.faces.el.ELContextImpl@24205} functionMapper = {com.sun.faces.facelets.compiler.NamespaceHandler@24284} variableMapper = {com.sun.faces.el.ELContextImpl$VariableMapperImpl@24221} variables = {java.util.HashMap@24289} size = 2 [0] = {java.util.HashMap$Entry@24292}"itemContact" -&gt; "/xhtml/test.xhtml @9,68 value=\"#{contactController.contact}\"" [1] = {java.util.HashMap$Entry@24295}"itemPerson" -&gt; "/xhtml/test.xhtml @10,66 value=\"#{contactController.person}\"" faceletElContext = {com.sun.faces.facelets.impl.DefaultFaceletContext@24199} faces = {com.sun.faces.context.FacesContextImpl@24200} ctx = {com.sun.faces.el.ELContextImpl@24205} facelet = {com.sun.faces.facelets.impl.DefaultFacelet@24151}"/xhtml/person.xhtml" faceletHierarchy = {java.util.ArrayList@24206} size = 2 [0] = {com.sun.faces.facelets.impl.DefaultFacelet@23792}"/xhtml/test.xhtml" [1] = {com.sun.faces.facelets.impl.DefaultFacelet@24151}"/xhtml/person.xhtml" varMapper = {com.sun.faces.facelets.el.VariableMapperWrapper@24207} target = {com.sun.faces.el.ELContextImpl$VariableMapperImpl@24221} vars = {java.util.HashMap@24222} size = 1 [0] = {java.util.HashMap$Entry@24225}"personinner" -&gt; "/xhtml/person.xhtml @5,52 value=\"#{itemPerson}\"" </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload