title: p:ajax immediate="true" on UIInput does not skip validation
body: <h2>TL;DR</h2>
  
  <pre><code>&lt;h:form&gt;
      &lt;p:inputText required="true"&gt;
          &lt;p:ajax event="click" immediate="true" process="@this" update="@this" /&gt;
      &lt;/p:inputText&gt;
  &lt;/h:form&gt;
  </code></pre>
  
  <p>click causes validation, even if ajax is immediate. Why?</p>
  
  <hr>
  
  <p>I <strike>can't make</strike> finally made this work out:</p>
  
  <pre><code>&lt;h:form&gt;
      &lt;p:dataGrid var="elem" value="#{immediateTestBean.elements}" columns="3"&gt;
          &lt;f:facet name="footer"&gt;
              &lt;p:commandButton action="#{immediateTestBean.newElement}" immediate="true"
                  process="@namingcontainer" update="@namingcontainer" value="#{bundle.add}" /&gt;
          &lt;/f:facet&gt;
  
          &lt;h:panelGrid columns="3"&gt;
              &lt;p:selectOneMenu value="#{elem.type}"&gt;
                  &lt;p:ajax immediate="true" process="@form" update="@form" /&gt;
                  &lt;f:selectItem itemValue="text" itemLabel="#{bundle.text}" /&gt;
                  &lt;f:selectItem itemValue="date" itemLabel="#{bundle.date}" /&gt;
              &lt;/p:selectOneMenu&gt;
  
              &lt;h:panelGroup id="detail"&gt;
                  &lt;p:inputText value="#{elem.text}" required="true" rendered="#{elem.type == 'text'}"
                      validator="#{immediateTestBean.testValidate}" label="#{bundle.text}"&gt;
                      &lt;f:validateLength minimum="4" /&gt;
                  &lt;/p:inputText&gt;
                  &lt;p:calendar value="#{elem.date}" required="true" rendered="#{elem.type == 'date'}"
                      pattern="dd/MM/yyyy" readonlyInput="true" showOn="button"
                      validator="#{immediateTestBean.testValidate}" label="#{bundle.date}" /&gt;
              &lt;/h:panelGroup&gt;
  
              &lt;p:commandButton action="#{immediateTestBean.removeElement(elem)}" immediate="true"
                  process="@namingcontainer" update="@namingcontainer" value="#{bundle.remove}" /&gt;
          &lt;/h:panelGrid&gt;
      &lt;/p:dataGrid&gt;
  
      &lt;p:spacer height="10" style="display: block" /&gt;
  
      &lt;p:commandButton process="@form" update="@form" value="#{bundle.submit}" /&gt;
  &lt;/h:form&gt;
  </code></pre>
  
  <p>Everything works fine, except <code>&lt;p:ajax immediate="true" process="@form" update="@form" /&gt;</code> which does not behave as <code>UICommand</code>s do: validation occurs (but in <code>APPLY_REQUEST_VALUES</code> phase).</p>
  
  <p>I tried to dig inside JSF source code and noted that <code>UICommand</code>s call <code>facesContext.renderResponse()</code> in default <code>ActionListenerImpl.processAction()</code>, so I tried with:</p>
  
  <p><code>&lt;p:ajax listener="#{facesContext.renderResponse}" immediate="true" process="@form" update="@form" /&gt;</code></p>
  
  <p>Now, when I select 'date', validation is skipped, but <code>&lt;h:panelGroup id="detail"&gt;</code> is not updated.</p>
  
  <p>Then, I tought that skipping validation caused skip of update model too, so I called:</p>
  
  <p><code>&lt;p:ajax listener="#{immediateTestBean.skip}" immediate="true" process="@form" update="@form" /&gt;</code></p>
  
  <p>And finally it works.</p>
  
  <p>But two questions arose:</p>
  
  <ol>
  <li><strong>Why is this so complicated?</strong></li>
  <li><strong>Is there a simpler way to achieve the same behavior?</strong></li>
  </ol>
  
  <p>However, here is the code of the bean:</p>
  
  <pre><code>@ManagedBean
  @ViewScoped
  public class ImmediateTestBean implements Serializable
  {
      private static final long serialVersionUID = 1L;
  
      private static final Logger logger = LoggerFactory.getLogger(ImmediateTestBean.class);
  
      public class Element
      {
          private String type = "text";
  
          private String text;
  
          private Date date;
  
          public String getType()
          {
              return type;
          }
  
          public void setType(String type)
          {
              this.type = type;
          }
  
          public String getText()
          {
              return text;
          }
  
          public void setText(String text)
          {
              this.text = text;
          }
  
          public Date getDate()
          {
              return date;
          }
  
          public void setDate(Date date)
          {
              this.date = date;
          }
      }
  
      private final List&lt;Element&gt; elements = new ArrayList&lt;&gt;();
  
      public void testValidate(FacesContext context, UIComponent component, Object value)
      {
          logger.debug("phase: {}", context.getCurrentPhaseId());
      }
  
      public void skip()
      {
          logger.debug("Faces.getCurrentPhaseId(): {}", Faces.getCurrentPhaseId());
  
          UIInput component = (UIInput) Components.getCurrentComponent();
          logger.debug("component: {}", component);
  
          FacesContext context = Faces.getContext();
          component.validate(context);
          if(component.isValid())
          {
              component.updateModel(context);
          }
  
          Faces.renderResponse();
      }
  
      public void newElement()
      {
          elements.add(new Element());
      }
  
      public void removeElement(Element elem)
      {
          elements.remove(elem);
      }
  
      public List&lt;Element&gt; getElements()
      {
          return elements;
      }
  }
  </code></pre>