0
votes

I am learning JSF + Seam Framework + AJAX4JSF and building an application. My application has an input field called barcode which the user scans/inputs. Once the barcode is inputted, I make an ajax call using ajax4jsf to call the listener method. The listener method should validate the inputted barcode. It validates the barcode against the database and if it is invalid, it returns a FacesMessage. If the barcode is valid, then it updates the database with the barcode and return to JSF. The render block mentioned in the ajax4jsf should be called to render the page and the javascript method mentioned in oncomplete should be executed.

My code is as follows.

JSF code:

<html ....
  xmlns:a="http://richfaces.org/a4j"
  xmlns:p="http://primefaces.org/ui">   

//javascript function
function renderPlate(data) {
    ......          
}

<h:form id="myForm">
    <h:outputText value="Scan barcode:" />
    <h:inputText id="inputBarcode" value="#{myBean.inputBarcode}"> 
        <a:ajax event="change" render="plateGrid" listener="#{myBean.addBarcodedItemToCheckout}" oncomplete="renderPlate()"/>
    </h:inputText>
</h:form>

<a:outputPanel id="plateGrid">
    <h:messages id="messages" style="color:red;margin:8px;" autoUpdate="true"/>
    ....
</a:outputPanel>

My Seam Bean:

@Scope(ScopeType.CONVERSATION)
@Name("plateMakerBean")
@Stateless 
public class PlateMakerAction {//implements PlateMaker {

private String inputBarcode;

public void addBarcodedItemToPlate() {

    boolean invalidBarcode = false;

    List<Item> items = em.createQuery("select m from Item i where barcode= #{inputBarcode}").getResultList();

    if(items == null || items.size() == 0) {
        invalidBarcode = true;
    } 

    if(invalidBarcode == true) {
        FacesMessages.instance().add("Barcode is not a valid barcode.");
    }
}

}

The question/problem I am facing is this:

  1. When I return the FacesMessages, the javascript method mentioned in the oncomplete is still getting executed. Is there any way we can do validations in Ajax actionlistener method and avoid calling the oncomplete method?

  2. Can we do input validations in the ajax action listener method? Is this the right approach to do it? I was going through some stackoverflow questions and some have suggested that we should use jsf custom validators instead? Is custom validators the only way to validate input values which has some business logic to validate it? I tried adding custom validator and when i do, the custom validator is getting executed and the error gets displayed. But, a:ajax actionlistener is not getting called which is what i expect. But, the section mentioned to be rendered in render (plateGrid) is not rendered. But, the javascript code mentioned in the oncomplete (renderData) is called even if the customer validator returns a validation error. My JSF code with custom validator, my customer validator method is below:

JSF code with custom validator

<h:form id="myForm">
    <h:outputText value="Scan barcode:" />
    <h:inputText id="inputBarcode" value="#{myBean.inputBarcode}" immediate="true"> 
        <f:validator validatorId="barcodeValidator" />
        <a:ajax event="change" render="plateGrid" listener="#{myBean.addBarcodedItemToCheckout}" oncomplete="renderPlate()"/>
    </h:inputText>
</h:form>

<a:outputPanel id="plateGrid">
    <h:messages id="messages" style="color:red;margin:8px;" autoUpdate="true"/>
    ....
</a:outputPanel>

My custom validator:

@Name("barcodeValidator")
@Scope(ScopeType.CONVERSATION)
@org.jboss.seam.annotations.faces.Validator
@BypassInterceptors
public class BarcodeValidator implements Validator, Serializable {

    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {

        if(value != null) {
        String barcode = value.toString();

        if(barcode.equals("M0"))
            throw new ValidatorException(new FacesMessage("Invalid Barcode"));

        }
    }
}

Any help and advice would be much appreciated.

2

2 Answers

0
votes

It is possible to check validation before oncomplete, It might be good idea to create an ajax4jsf command button with display none as follow :

<div style="display:none">
<a:commandButton data="#{albumManager.validationSuccess}"
                                value="#{messages['album.store']}"
                                actionListener="#{albumManager.addAlbum(album)}"
                                id="storebutton"
                                reRender="treePanel, mainArea, menu" 
oncomplete = "if(data)$('albumModalPanel').component.hide()" styleClass="album"/>
</div>

after reading barcode, create a js function and click on the button with jQuery

<h:inputText id="inputBarcode" value="#{myBean.inputBarcode}" immediate="true" onchange="onChangeBarcode();"> 

function onChangeBarcode()
{
  jQuery("storebutton").click();
}

I hope it helps. for more information please visit richfaces + seam sample (photo album)

0
votes

You can do this...

<h:inputText id="inputBarcode" value="#{myBean.inputBarcode}"> 
    <a:ajax event="change" render="plateGrid" 
        listener="#{myBean.addBarcodedItemToCheckout}" 
        oncomplete="if(!#{myBean.invalidBarcode}){renderPlate()}"/>
</h:inputText>

Using a JSF validator would be the 'correct' way to do this though

BTW: your PlateMakerAction component is annotated with @Stateless as well as @Scope(ScopeType.CONVERSATION), this is wrong, you should get rid of the @Stateless annotation unless you really want it to be stateless which I doubt