Reputation: 1870
I have a js-function codeAddress()
which completes an address via google maps javascript api and sets a global js-variable valid = true
, if the full address was recognized. It also updates the value of a <p:inputText>
with the completed address.
Now I have one <p:commandButton>
that I want to
codeAddress()
valid = true
submit form so address in <p:inputText>
is saved in addressBean
andvalid = false
trigger jsf-validator messagesUnfortunately my setup still does not work with valid
as the essential value to determine if next page. It seems, that the value is submitted to bean too late.
this is my jsf code:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<script src="https://maps.googleapis.com/maps/api/js?v=3&sensor=false"></script>
</h:head>
<h:body>
<h:outputScript name="jsf.js" library="javax.faces" target="head"/>
<h:outputScript library="js" name="bugMaps.js" />
<body onload="initialize()" />
<h:form id="addressForm" onsubmit="codeAddress();">
<p:inputText id="address" required="true"
validatorMessage="Please enter a valid address." validator="#{addressBean.validate()}">
<p:ajax update=":addressForm:addressValidate" />
</p:inputText>
<h:message id="addressValidate" for=":addressForm:address"/>
<p:commandButton value="submit"
onclick="codeAddress();"/>
<p:inputText id="fullAddress" value="#{addressBean.fullAddress}" />
<p:inputText id="valid" value="#{addressBean.valid}" />
</h:form>
</h:body>
</html>
this is the bugMaps.js:
var geocoder;
var map;
var valid;
function initialize() {
geocoder = new google.maps.Geocoder();
}
function codeAddress() {
var address = (document.getElementById('addressForm:address').value + ", Germany");
geocoder.geocode({'address' : address},function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var latLong = results[0].geometry.location;
var latitude = results[0].geometry.location.lat();
var longitude = results[0].geometry.location.lng();
var country, postal_code, locality, street_number, route;
for (i = 0; i < results[0].address_components.length; ++i) {
var component = results[0].address_components[i];
if (!locality && component.types.indexOf("locality") > -1)
locality = component.long_name;
else if (!postal_code && component.types.indexOf("postal_code") > -1)
postal_code = component.long_name;
else if (!country && component.types.indexOf("country") > -1)
country = component.long_name;
else if (!street_number && component.types.indexOf("street_number") > -1)
street_number = component.long_name;
else if (!route && component.types.indexOf("route") > -1)
route = component.long_name;
}
if (typeof latLong != "undefined"
&& typeof latitude != "undefined"
&& typeof longitude != "undefined"
&& typeof route != "undefined"
&& typeof street_number != "undefined"
&& typeof postal_code != "undefined"
&& typeof locality != "undefined"
&& typeof country != "undefined"){
valid = true;
}
else{
valid=false;
};
document.getElementById('addressForm:fullAddress').value = results[0].formatted_address;
jsfSubmit();
}
else{
alert('Geocode was not successful for the following reason: '+ status);
valid=false;
}
});
};
function navigateToAnotherPage(data){
if(valid){
document.location.href='nextPage.xhtml';
}
};
function jsfSubmit(){
jsf.ajax.request(this, event, {execute:"@form", onevent:navigateToAnotherPage});
alert("nothing"); // FIXME: without this useful alert bean keeps old values and display them on nextPage
};
this is my AddressBean.java:
package hoho.main.managebean;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;
import javax.inject.Named;
import com.sun.faces.util.MessageFactory;
@Named
@SessionScoped
public class AddressBean implements Serializable {
private static final long serialVersionUID = 1L;
private String fullAddress;
private boolean valid = false;
public void validate(FacesContext context,
UIComponent toValidate, Object value) {
if(!valid){
String client = toValidate.getClientId(context);
FacesMessage msg = MessageFactory.getMessage(UIInput.REQUIRED_MESSAGE_ID, client);
throw new ValidatorException(msg);
}
}
public String getFullAddress() {
return fullAddress;
}
public void setFullAddress(String fullAddress) {
this.fullAddress = fullAddress;
}
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
}
this is the nextPage.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
</h:head>
<h:body>
<h:outputText value="#{addressBean.fullAddress}" />
</h:body>
</html>
Upvotes: 2
Views: 1991
Reputation: 780
You can use jsf.ajax.request
in your javascript to submit your form. An example is
function navigateToAnotherPage(data){
//When the request is completed, we are safe to navigate to another page.
if(data.status == "success"){
alert("callback is invoked");
//Go to another page
document.location.href='destpage.xhtml';
}
}
function jsfSubmit(){
alert("Do your logic here");
jsf.ajax.request(this, event, {execute:"@form", onevent:navigateToAnotherPage});
}
And bind this function to a jsf component
<h:commandButton type="button" value="javascriptSubmit" onclick="jsfSubmit()"/>
A complete reference is here:http://docs.oracle.com/cd/E17802_01/j2ee/javaee/javaserverfaces/2.0/docs/js-api/symbols/jsf.ajax.html
However, I don't know how to call a backing bean method through javascript. So I always use two buttons. The javascript process logic when I click the first button , and after that it trigger the click event on the second button that submits the form and navigate to another page. And of course the second button would be display:none
. Also, it is kinda simpler.
Besides, the validation does not work because validation always happen before the backing bean is updated. I don't see any link between the javascript var valid
in javascript and the boolean valid
in the backing bean. Howver, if you update <p:inputText id="valid" value="#{addressBean.valid}" />
using javascript, submit the input field, update the backing bean valid
and use this boolean variable for validation, then the validation will always fail. This is because before boolean valid
is updated (for example, true), the validation method is first called(so the validation method may be using an out-of-date valid
.
The walk around would be, you attach the validator to <p:inputText id="valid" value="#{addressBean.valid}" />
rather than <p:inputText id="address" required="true"...../>
. Now, you can get the value of the input field using value
in validate(FacesContext context, UIComponent toValidate, Object value)
. The value here is the value you put(or javascript put) in the input field on the page (and therefore it is always up-to-date).
Please note that, by this time the valid
in the backing bean is not updated yet. If the validation is OK, then valid
is updated and set to value
you just got. If the validation fails, then valid
is not updated.
In a word, the flow would be, 0. build/restore the component tree. 1. conversion, validation. 2. update the values in the backing bean. 3. call backing bean method 4. render the page.
Upvotes: 2