Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I did some investigation and debugging in the last two days and this is my result.</p> <p>First of all, I simplified example to omit composite component and to have it as simple as possible.</p> <p>Managed bean (TesterBean2.java)</p> <pre><code>package cz.test; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; @ManagedBean @RequestScoped public class TesterBean2 { // Simple DataStore (in real world EJB) private static String storedSomeValue = null; private String someValue; public TesterBean2() { } public String storeValue() { storedSomeValue = someValue; return "index"; } public String eraseValue() { storedSomeValue = null; return "index"; } public String getSomeValue() { someValue = storedSomeValue; return someValue; } public void setSomeValue(String someValue) { this.someValue = someValue; } } </code></pre> <p>Testing page (index.xhtml)</p> <pre><code>&lt;?xml version='1.0' encoding='UTF-8' ?&gt; &lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt; &lt;html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"&gt; &lt;h:head&gt; &lt;title&gt;Testing page&lt;/title&gt; &lt;/h:head&gt; &lt;h:body&gt; &lt;h:form&gt; &lt;h:inputText id="fieldValue" requiredMessage="Field is mandatory" title="#{testerBean2.someValue}" value="#{testerBean2.someValue}" required="true" style="#{empty testerBean2.someValue ? 'background-color: yellow;' : ''}"/&gt; &lt;h:commandButton value="Store" action="#{testerBean2.storeValue}"/&gt; &lt;h:commandButton value="Erase" action="#{testerBean2.eraseValue}"/&gt; &lt;/h:form&gt; &lt;/h:body&gt; </code></pre> <p></p> <p>Where is the problem? I think it is problem of com.sun.faces.renderkit_html_basic.TextRenderer and its method getEndTextToRender:</p> <pre><code>protected void getEndTextToRender(FacesContext context, UIComponent component, String currentValue) throws IOException { ResponseWriter writer = context.getResponseWriter(); assert(writer != null); boolean shouldWriteIdAttribute = false; boolean isOutput = false; String style = (String) component.getAttributes().get("style"); String styleClass = (String) component.getAttributes().get("styleClass"); String dir = (String) component.getAttributes().get("dir"); String lang = (String) component.getAttributes().get("lang"); String title = (String) component.getAttributes().get("title"); if (component instanceof UIInput) { writer.startElement("input", component); writeIdAttributeIfNecessary(context, writer, component); writer.writeAttribute("type", "text", null); writer.writeAttribute("name", (component.getClientId(context)), "clientId"); // only output the autocomplete attribute if the value // is 'off' since its lack of presence will be interpreted // as 'on' by the browser if ("off".equals(component.getAttributes().get("autocomplete"))) { writer.writeAttribute("autocomplete", "off", "autocomplete"); } // render default text specified if (currentValue != null) { writer.writeAttribute("value", currentValue, "value"); } // Rest of code omitted } </code></pre> <p>A <b>currentValue</b> parameter is explicitly passed into this method called from com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd</p> <pre><code>@Override public void encodeEnd(FacesContext context, UIComponent component) throws IOException { rendererParamsNotNull(context, component); if (!shouldEncode(component)) { return; } ResponseWriter writer = context.getResponseWriter(); assert(writer != null); // NOTICE currentValue getter String currentValue = getCurrentValue(context, component); if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Value to be rendered {0}", currentValue); } // NOTICE currentValue getEndTextToRender(context, component, currentValue); } </code></pre> <p>and if we look closer at a method getCurrentValue()</p> <pre><code>/** * @param context the FacesContext for the current request * @param component the UIComponent whose value we're interested in * * @return the value to be rendered and formats it if required. Sets to * empty string if value is null. */ protected String getCurrentValue(FacesContext context, UIComponent component) { if (component instanceof UIInput) { Object submittedValue = ((UIInput) component).getSubmittedValue(); if (submittedValue != null) { // value may not be a String... return submittedValue.toString(); } } String currentValue = null; Object currentObj = getValue(component); if (currentObj != null) { currentValue = getFormattedValue(context, component, currentObj); } return currentValue; } </code></pre> <p>the property returned by <b>getSubmittedValue()</b> is filled in Restore View phase (if Process validation phase skips to Render response phase). As a consequence we get "updated" value which was passed from user only for <b>value</b> attribute, the rest remain unaltered.</p> <p>In case of successful Process Validation phase, which does not cause direct skip to Render Response phase (if user fills in any not null value), a new constructor of HtmlInputText is called and <b>style, title etc.</b> attributes are filled from the scratch. These attributes are filled from managed bean which was updated with proper data in phase Update Model Values.</p> <p>OK, this is not a bug, but the feature. It only affirms my thesis that something smells in the sentence: "If the request is a postback and errors were encountered during the apply request values phase, process validations phase, or update model values phase, the original page is rendered during Render response phase".</p> <p>Any clue how to solve this situation if I really long for yellow background for mandatory fields?</p> <p>Updated project is here: <a href="http://www.221b.cz/so/JSFTester2.zip" rel="nofollow noreferrer">http://www.221b.cz/so/JSFTester2.zip</a></p>
 

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