Skip to main content

JSF, Primefaces - Invoking Application Code Even When Validation Failed


A use case

I have a form which has requirements as follow:
- There are some mandatory fields.
- Validation is triggered when changing value on each field.
- A button "Next" is enable only when all fields are entered. It turns to disabled if any field is empty.

My first approach

I defined a variable "isDisableNext" at a backend bean "Controller" for dynamically disabling/enabling the "Next" button by performing event "onValueChange", but, it had a problem:
<h:form id="personForm">
   <p:outputLabel value="First Name" for="firstName"/>
   <p:inputText id="firstName" value="#{person.firstName}"
     required="true">
    <p:ajax event="change" listener="#{controller.onValueChange}" update="nextButton"/>
   </p:inputText>
   
   <p:outputLabel value="Last Name" for="lastName"/>
   <p:inputText id="lastName" value="#{person.lastName}"
     required="true">
    <p:ajax event="change" listener="#{controller.onValueChange}" update="nextButton"/>
   </p:inputText>
   
   <p:commandButton id="nextButton" actionListener="#{controller.onNext}" update="personForm" disabled="#{controller.isDisabledNext}"/>
</h:form>
Due to JSF lifecyle, the application code of Ajax "onValueChange" (at phase Invoke Application) is never invoked when validation failed. How could we update the value "isDisableNext"?
src: http://docs.oracle.com/javaee/5/tutorial/doc/bnaqq.html

My new approach: I don't try to update the backend bean at phase Invoke Application anymore but use custom validator

What is it? And, why is it possible?
- At phase Process Validation, it always calls my application code even when validation failed
- I handle enabling/disabling the button with JSF component tree instead of a backend bean.

The previous implementation turns to the following:
- Don't use required="true" because it won't invoke customer validators when a field's submitted value is empty. Then I need to add the "*" manually with "span class="ui-outputlabel-rfi".
- Use custom validator with "f:validator"
- Pass component button "Next" with "f:attribute" and "binding"
<h:form id="personForm">
   <p:outputLabel value="First Name" for="firstName">
    <span class="ui-outputlabel-rfi">*</span>
   </p:outputLabel>
   <p:inputText id="firstName" value="#{person.firstName}">
    <p:ajax event="change" listener="#{controller.onValueChange}" update="nextButton"/>
    <f:validator validatorId="requiredFieldValidator"/>
    <f:attribute name="nextButton" value="#{nextButton}"/>
   </p:inputText>
   
   <p:outputLabel value="Last Name" for="lastName">
    <span class="ui-outputlabel-rfi">*</span>
   </p:outputLabel>
   <p:inputText id="lastName" value="#{person.lastName}">
    <p:ajax event="change" listener="#{controller.onValueChange}" update="nextButton"/>
    <f:validator validatorId="requiredFieldValidator"/>
    <f:attribute name="nextButton" value="#{nextButton}"/>
   </p:inputText>
   
   <p:commandButton id="nextButton" actionListener="#{controller.onNext}" update="personForm" binding="#{nextButton}"/>
</h:form>
The custom validator looks like:
@FacesValidator(value = "requiredFieldValidator")
public class RequiredFieldValidator implements Validator{

 public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
  CommandButton nextButtonUi = (CommandButton) component.getAttributes().get("nextButton");
  Map<String, String> requestParameterMap = context.getExternalContext().getRequestParameterMap();
  String firstName = requestParameterMap.get(getClientId("firstName"));
  String lastName = requestParameterMap.get(getClientId("lastName"));
  if(StringUtils.isEmpty(firstName) || StringUtils.isEmpty(lastName)) {
   nextButtonUi.setDisabled(true);
  }else {
   nextButtonUi.setDisabled(false);
  }
  
  RequestContext.getCurrentInstance().update(getClientId("nextButton"));
 }

}
How is your approach? Leave your comment down below!

Comments

Popular posts from this blog

My impression of Google I/O 2016

I have not yet had a chance to attend this event but I just watched it on Youtube  as usual. :) Google Assistant You might be very familiar to Google Now if you're Android fan. I saw that Google Assistant is like an upgrade to Google Now. It looks smarter because we are able to make a conversation with Google rather than just saying "Ok Google" and getting the tasks done as Google Now. That means Google Assistant can understand our personal words in our contexts. For example: I : Who is Google CEO? Google : Sundar Pichai I : Show me his awards  => Here I don't need to say "Show me Sundar Pichai awards", because I don't know to pronounce his name correctly. :) I am thinking about that we can compare Google Assistant to Siri of Apple. Google Home At Google I/O 2015 last year, Sundar mentioned about working on a exciting project with Internet of Things and I think Google Home is a result. Working with Google Assistant, Google ho...

Styling Sort Icons Using Font Awesome for Primefaces' Data Table

So far, Primefaces has used image sprites for displaying the sort icons. This leads to a problem if we want to make a different style for these icons; for example, I would make the icon "arrow up" more blurry at the first time the table loading because I want to highlight the icon "arrow down". I found a way that I can replace these icons with Font Awesome icons. We will use "CSS Pseudo-classes" to achieve it. The hardest thing here is that we should handle displaying icons in different cases. There is a case both "arrow up" and "arrow down" showing and other case is only one of these icons is shown. .ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s { background-image: none; margin-left: 5px; font-size: 1.1666em; position: relative; } .ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s:not(.ui-icon-triangle-1-s)::before { content: "\f106"; font-family: "FontAwesome"; position: ...

Selenium - Override javascript functions to ignore downloading process

I have got an issue with downloading process on IE 8. This browser blocks my automatic-download functionality on my app so that I could not work with my test case any more after that. In my case, I didn't care about the file is downloaded or not, I just focus on the result after downloading process finished successfully. Therefore, I found a solution to ignore this process so that I can work normally. I use Primefaces, here is the remote command to trigger the download process <p:remoteCommand name="cmdGenerateDocument" actionListener="#{logic.onGenerateDocument}" update="xrflDocumentCreationPanel" oncomplete="clickDownloadButton();"/> The following is my test case: @Test public void shouldUpdateStep6ToWarningIfStep1IsValidAfterFinished(){ MainPage mainPage = new MainPage(); waitForLoading(mainPage.getDriver()); EmployeeDetailPage empDetailPage = new EmployeeDetailPage(); waitForLoading(empDetailPage.getDriver()); e...

Monday vhandit #2

  Introduction to OpenLDAP directory service "A directory is a specialized database specially designed for searching and browsing, in addition to supporting basic lookup and update functions" A directory service can be local, providing a restricted context; or global, providing service to a much broader context. Curlie is a good example of a directory service. LDAP (Lightweight Directory Access Protocol) is a protocol for accessing directory services , specifically X.500-based directory services. OpenLDAP is an open-source implementation of LDAP. Writing system software: code comments I have read Clean Code (by Uncle Bob) and I thought that I should void comments since the code it explains its implementation itself. That is right but not always true. In this post, the author categorized the comments into 9 types. Only "trivial comments" and "backup comments" are the ones that should be avoided. I myself agree with "writing good comments is harder tha...

Creating a Chatbot with RiveScript in Java

Motivation "Artificial Intelligence (AI) is considered a major innovation that could disrupt many things. Some people even compare it to the Internet. A large investor firm predicted that some AI startups could become the next Apple, Google or Amazon within five years"   - Prof. John Vu, Carnegie Mellon University. Using chatbots to support our daily tasks is super useful and interesting. In fact, "Jenkins CI, Jira Cloud, and Bitbucket" have been becoming must-have apps in Slack of my team these days. There are some existing approaches for chatbots including pattern matching, algorithms, and neutral networks. RiveScript is a scripting language using "pattern matching" as a simple and powerful approach for building up a Chabot. Architecture Actually, it was flexible to choose a programming language for the used Rivescript interpreter like Java, Go, Javascript, Python, and Perl. I went with Java. Used Technologies and Tools Oracle JDK 1.8...