JSF - Example login-ajax - Why it send the form? - ajax

Im trying JSF framework. Im a beginner. So i start with a easy tutorial. This is the code :
<h:form id="form1" prependId="false">
<h:outputScript name="jsf.js" library="javax.faces" target="head" />
<h:inputText value="#{user.name}" id="name"/>
<h:inputSecret value="#{user.password}" id="password"/>
<h:outputText value="#{user.greeting}" id="out"/>
<br/>
<h:commandButton value="Login" type="button" onclick="jsf.ajax.request(this, event, {execute: 'name password', render: 'out'}); return false;" />
</h:form>
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean(name = "user")
#RequestScoped
public class UserBean {
private String name = "";
private String password;
public String getName() { return name; }
public void setName(String newValue) { name = newValue; }
public String getPassword() { return password; }
public void setPassword(String newValue) { password = newValue; }
public String getGreeting() {
if(name.length()==0) {
return "Inserisci nickname";
} else {
return "Benvenuto "+name;
}
}
}
In "less words" (when i click on button) :
1 - thanks to execute: 'name password, I call the setName and setPassword (JavaBean methods) with an asynch call;
2 - than, thanks to render: 'out' I call getGreeting
So, execute will be translate as SET and render as GET.
What I would like to know is if I have well understand the process.
Because I have a misunderstanding : if I scan the traffik from client to server, I see that (trought the ajax call) it sends from client to server the form1, name and password (and, as MAGIC, name and password have got the value inserted by me).
So how is this possible?
Thanks to all!

That isn't magic. That's normal. Those values are entered in webbrowser. JS/Ajax has access to those values. JS/Ajax will send those values along. How else should server side/JSF know about the newly entered values?

Related

Primefaces Theme change to user specific

When I use the below code it's working fine and change the theme, but the problem is if one of the user logged and change the theme it effects to the every user in the system. what I want is to effect the theme only for the particular user but not for every one.
Web.xml
<context-param>
<param-name>primefaces.THEME</param-name>
<param-value>#{settingsController.userTheme}</param-value>
</context-param>
primeface (.xhtml)
<h:outputLabel for="userTheme" value="Theme Name *:" style="width: 300px"/>
<p:selectOneMenu id="userTheme" value="#{settingsController.userTheme}" style="width:200px"
required="true" requiredMessage="Theme Name is Required" >
<f:selectItems value="#{settingsController.themeMap}"/>
</p:selectOneMenu>
SettingsController.java class
#ManagedBean(name = "settingsController")
#SessionScoped
#Controller
public class SettingsController {
private String userTheme = "start" ;
private Map<String , String> themeMap ;
#PostConstruct
public void init (){
setThemeMapInit( );
}
public String getUserTheme() {
return userTheme;
}
public void setUserTheme(String userTheme) {
this.userTheme = userTheme;
}
public Map<String, String> getThemeMap() {
return themeMap;
}
public void setThemeMapInit() {
themeMap = new LinkedHashMap<String, String>();
themeMap.put("Aristo", "aristo");
themeMap.put("After-noon", "afternoon");
themeMap.put("After-Work", "afterwork");
themeMap.put("Black-Tie", "black-tie");
themeMap.put("Blitzer", "blitzer");
themeMap.put("Bluesky", "bluesky");
themeMap.put("Bootstrap", "bootstrap");
themeMap.put("Casablanca", "casablanca");
themeMap.put("Cupertino", "cupertino");
themeMap.put("Dark-Hive", "dark-hive");
themeMap.put("Delta", "delta");
themeMap.put("Excite-Bike", "excite-bike");
themeMap.put("Flick", "flick");
themeMap.put("Glass-X", "glass-x");
themeMap.put("Home", "home");
themeMap.put("Hot-Sneaks", "hot-sneaks");
themeMap.put("Humanity", "humanity");
themeMap.put("Overcast", "overcast");
themeMap.put("Pepper-Grinder", "pepper-grinder");
themeMap.put("Redmond", "redmond");
themeMap.put("Rocket", "rocket");
themeMap.put("Sam", "sam");
themeMap.put("Smoothness", "smoothness");
themeMap.put("South-Street", "south-street");
themeMap.put("Start", "start");
themeMap.put("Sunny", "sunny");
themeMap.put("Swanky-Purse", "swanky-purse");
themeMap.put("UI-Lightness", "ui-lightness");
}
public void setThemeMap(Map<String, String> themeMap) {
this.themeMap =themeMap;
}
public void sumbitUserSettings (){
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
try {
ec.redirect(((HttpServletRequest) ec.getRequest()).getRequestURI());
} catch (IOException ex) {
Logger.getLogger(SettingsController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Every Spring bean is a Singletone by default, that's why all users are affected, despite of #SessionScoped.
You can't use #ManagedBean and #Controller at the same time, see why.
The best way to combine Spring and JSF in the same app, is to use Joinfaces.
With Joinfaces your bean will end up looking like this
#Named
#SessionScoped
public class SettingsController {
Related:
JSF vs. Spring MVC

Why #ViewScoped Managed Bean's #PostConstruct init() method never called?

I am newbie to JSF/Java ee7 and tried to test some features to understand how things work.
I used templating for testing purposes. Here is the relevant JSF template client:
<ui:define name="content">
<h:panelGroup class="keret" id="tartalom">
<h:form id="email" rendered="#{emailManagedBean.urlap}">
<h:messages/>
<h:outputLabel for="emailbox" value="Add meg az email címed, és juss hozzá bestsellerünkhöz 5Ft-ért"/>
<h:inputText value="#{emailManagedBean.newEmail.email}"/>
<h:commandButton actionListener="#{emailManagedBean.saveEmail()}" value="Mentés">
<f:ajax immediate="true" execute="#form" render=":tartalom"/>
</h:commandButton>
</h:form>
<h:panelGroup id="szoveg" rendered="#{not emailManagedBean.urlap}">
<h3>Köszönjük!</h3>
<p>
Hamarosan emailt fogsz kapni tőlünk. Kérlek, ellenőrizd a levélszemét, illetve spam mappákban is a tőlünk kapott levelet. A levlében lévő linkre kattintva hozzájuthatsz az ajándékodhoz.
</p>
</h:panelGroup>
</h:panelGroup>
</ui:define>
My managed bean:
#Named(value = "emailManagedBean")
#ViewScoped
public class emailManagedBean implements Serializable {
#EJB
EmailsFacadeLocal emailFacade;
private Emailcamp newEmail;
private boolean urlap;
public Emailcamp getNewEmail() {
return newEmail;
}
public void setNewEmail(Emailcamp newEmail) {
this.newEmail = newEmail;
}
#PostConstruct
public void init() {
newEmail = new Emailcamp();
urlap=true;
}
public boolean isUrlap() {
return urlap;
}
public void setUrlap(boolean urlap) {
this.urlap = urlap;
}
public void validateEmail(FacesContext context, UIComponent comp,
Object value) {
String input = (String) value;
EmailValidator validator = EmailValidator.getInstance();
if (!validator.isValid(input)) {
((UIInput) comp).setValid(false);
FacesMessage message = new FacesMessage(
"Helytelen email cím formátum");
context.addMessage(null, message);
} else {
if(!emailFacade.exist(newEmail.getEmail())){
((UIInput) comp).setValid(true);
}
else {
context.addMessage(null, new FacesMessage("Már megadtad korábban az email címed"));
}
}
}
public void saveEmail() {
/*Get current date and time*/
Calendar c = Calendar.getInstance();
newEmail.setRecemaildate(c.getTime());
/*Get Referral*/
HttpServletRequest hr = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
newEmail.setReferer((hr.getHeader("referer")==null) ? "not available" : hr.getHeader("referer") );
newEmail.setStatus(Status.NOT_SENT.toString());
emailFacade.create(newEmail);
urlap=false;
}
}
The expected behavior is that the init function set urlap boolean variable to true so the h:form should be shown as there is conditional rendering on the view side.
Later the customer clicks on Save ("Mentés") and the new email address and some data will be stored in a database. After the ajax call I urlap boolean varaible is set to false not to render the form again but render a thank you message.
Unfortunately the init function is never called so the boolean variable never set to true. As a consequences my thanks message is shown without collecting the email.
The second strange thing which is the consequence of the ajax component (I guess) that I got an error message: outputScript with no library, no name, and no body content. I did not declare any outputScript in my xhtml.

How to call ajax in a jsf page without making a full server round trip?

I am writting a simple user registration process.What I want to do is that when the user enters a username, I want to check if the username has already been taken by someone else. If it has been, then I want to show a message under the input field, asking the user to choose a different username.
I have already written the database query/ejb/rest etc...
What I want to know is ... How do I make a call to the backend (ejb/rest) and show the message on the screen without having to make a full round trip to the server and refreshing the entire page?
Update 1:
I have changed my code to look like the example from Simon.(Don't know why it did not appear before).
<h:form>
<h:panelGrid columns="2">
<h:outputLabel for="username" value="Please enter a user name :" />
<h:inputText id="username" value="#{userNameCheckBean.signInName}"
autocomplete="off" size="20" onfocus="blankOut();" required="true"
requiredMessage="Error: Username is required." maxlength="25">
<f:ajax execute="#this" render="message" listener="#{userNameCheckBean.verifyIfUsernameIsAlreadyTaken()}" />
</h:inputText>
<h:outputText rendered="#{userNameCheckBean.userExists}" id="message" value="#{userNameCheckBean.message}" />
</h:panelGrid>
<h:commandButton id="checkUser" action="#{userNameCheckBean.userExists}" value="Check">
</h:commandButton>
</h:form>
The confusing thing for me is, How do I call the verifyIfUsernameIsAlreadyTaken() method when the username inputText looses focus? and how do I update just the bit of the overall form? Is it a must to have a commandButton to submit the username field? I thought ajax was meant to make server calls in the background.
Update 2:
I have changed the .xhtml file to the one given below. I still have to click on the button to call the backing bean. It simply ignores the event. Any help will be much appriciated.
<h:form>
<h:panelGrid columns="2">
<h:outputLabel for="username" value="Please enter a user name :" />
<h:inputText id="username" value="#{userNameCheckBean.signInName}" autocomplete="off" size="20" onfocus="blankOut();" required="true" requiredMessage="Error: Username is required." maxlength="25">
<f:ajax execute="#this" event="blur" render="message" listener="#{userNameCheckBean.checkIfUsernameIsAlreadyTaken()}" />
</h:inputText>
<h:panelGroup>
<h:outputText rendered="#{userNameCheckBean.userExists}" id="message" value="#{userNameCheckBean.message}" />
</h:panelGroup>
</h:panelGrid>
<h:commandButton id="checkUser" action="#{userNameCheckBean.checkIfUsernameIsAlreadyTaken}" value="Check">
</h:commandButton>
</h:form>
Update 3:
Here is backing bean that works when I click on the Button.
package co.uk.dakia.beans;
import java.io.IOException;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.event.AjaxBehaviorEvent;
import javax.naming.NamingException;
import javax.validation.constraints.NotNull;
import example.UserManager;
import example.ServiceLocator;
#ManagedBean
#RequestScoped
public class UserNameCheckBean {
private static Logger logger = Logger.getLogger(UserNameCheckBean.class.getName());
#EJB(beanName = "UserManager", mappedName = "jndi:ext://example/UserManagerRemote")
UserManager userManager;
String signInName;
String password;
String message = "Message: ";
boolean userExists = false;
public void checkIfUsernameIsAlreadyTaken(AjaxBehaviorEvent event) throws NamingException, IOException {
userManager = (UserManager) ServiceLocator.locateService("UserManagerRemote");
logger.info("Checking if username [" + signInName + "] has already been taken by some other user");
userExists = userManager.userExists(signInName);
if(userExists) {
message = "Username is already taken!";
} else {
message = "";
logger.info("Username [" + signInName + "] is available.");
}
}
public void checkIfUsernameIsAlreadyTaken() throws NamingException, IOException {
checkIfUsernameIsAlreadyTaken(null);
}
public String getSignInName() {
return signInName;
}
public void setSignInName(String signInName) {
this.signInName = signInName;
}
public String getMessage() throws NamingException, IOException {
checkIfUsernameIsAlreadyTaken();
return message;
}
public void setMessage(String message) {
this.message = message;
}
public boolean isUserExists() {
return userExists;
}
public void setUserExists(boolean userExists) {
this.userExists = userExists;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
You did not provide any code so it is hard to tell you exactly what to do, but here's a template (assuming you're using JSF 2):
.xhtml:
<h:form>
...
<h:inputText id="username" value="#{myBean.username}">
<f:ajax execute="#this" render="username-already-taken-wrapper" listener="#{myBean.verifyIfUsernameIsAlreadyTaken()}"/>
</h:inputText>
<h:panelGroup id="username-already-taken-wrapper">
<h:outputText rendered="#{myBean.usernameAlreadyTaken}" value="Username is already taken!"/>
</h:panelGroup>
...
</h:form>
MyBean.java:
public void verifyIfUsernameIsAlreadyTaken()
{
usernameAlreadyTaken = verify(username);
}
private boolean verify(String username)
{
...
}
The trick is to use AJAX to submit only a part of your form (in this case: the username), and refresh only a part of the page (in this case: username-already-taken-wrapper). If the username is already taken, the message will show. If not, nothing will show.

Inserting custom tag in JSF Ajax response XML

Consoder the following code:
<h:commandButton value="do" action="#{testBacking.do}">
<f:ajax execute="#all" render="#all" listener="#{testBacking.listener}"/>
</h:commandButton>
I want to have a custom tag (with value based on server logic), in the Ajax response XML, something like the following:
<isValidationFailed> true </isValidationFailed>
I can use this data to re-enable the button (which was disabled when Ajax begin, to avoid double clicks) if validation is failed.
How can I achieve this (preferably without using any JSF 3rd party libraries)?
EDIT:
The example code, more precisely, should be like this:
<h:commandButton id="myButton" value="do" action="#{testBacking.do}">
<f:ajax execute="id1" render="id2 myButton" listener="#{testBacking.listener}"/>
</h:commandButton>
This is only possible with a custom PartialViewContext which you load into your JSF application using a PartialViewContextFactory. The custom PartialViewContext should in turn return a custom PartialResponseWriter on PartialViewContext#getResponseWriter(). In this custom PartialResponseWriter, you should be able to add extensions to the XML response by calling startExtension() and endExtension() in endDocument(). Something like:
#Override
public void endDocument() throws IOException {
Map<String, String> attributes = new HashMap<String, String>();
attributes.put("name1", "value1");
attributes.put("name2", "value2");
startExtension(attributes);
write("lorem ipsum");
endExtension();
super.endDocument();
}
This will then end up in the XML response as
<extension name1="value1" name2="value2">lorem ipsum</extension>
This is available and traversable by data.responseXML in jsf.ajax.addOnEvent() function.
Here's a full kickoff example how you could utilize it in your particular case:
MyPartialViewContextFactory which provides the custom partial view context:
public class MyPartialViewContextFactory extends PartialViewContextFactory {
private PartialViewContextFactory wrapped;
public MyPartialViewContextFactory(PartialViewContextFactory wrapped) {
this.wrapped = wrapped;
}
#Override
public PartialViewContext getPartialViewContext(FacesContext context) {
return new MyPartialViewContext(wrapped.getPartialViewContext(context));
}
}
MyPartialViewContext which provides the custom partial response writer:
public class MyPartialViewContext extends PartialViewContextWrapper {
private PartialViewContext wrapped;
private PartialResponseWriter writer;
public MyPartialViewContext(PartialViewContext wrapped) {
this.wrapped = wrapped;
this.writer = new MyPartialResponseWriter(wrapped.getPartialResponseWriter());
}
#Override
public PartialResponseWriter getPartialResponseWriter() {
return writer;
}
#Override
public void setPartialRequest(boolean isPartialRequest) {
wrapped.setPartialRequest(isPartialRequest);
}
#Override
public PartialViewContext getWrapped() {
return wrapped;
}
}
MyPartialResponseWriter which writes <extension id="myextension"> with the body as JSON):
public class MyPartialResponseWriter extends PartialResponseWriter {
public MyPartialResponseWriter(ResponseWriter wrapped) {
super(wrapped);
}
#Override
public void endDocument() throws IOException {
startExtension(Collections.singletonMap("id", "myextension"));
write("{\"validationFailed\": " + FacesContext.getCurrentInstance().isValidationFailed() + "}"); // Consider a JSON serializer, like Google Gson.
endExtension();
super.endDocument();
}
}
To get it to run, register the factory as follows in faces-config.xml:
<factory>
<partial-view-context-factory>com.example.MyPartialViewContextFactory</partial-view-context-factory>
</factory>
Here's how you can access, parse and use the <extension id="myextension"> in your jsf.ajax.addOnEvent():
jsf.ajax.addOnEvent(function(data) {
if (data.status == "success") {
var args = JSON.parse(data.responseXML.getElementById("myextension").firstChild.nodeValue);
if (args.validationFailed) {
// ...
}
else {
// ...
}
}
});
However, your particular functional requirement can be achieved in a different, likely simpler, manner. Just let the ajax request update the button itself and let the button's disabled attribute evaluate true when there's means of a successful postback.
<h:commandButton id="myButton" value="do" action="#{testBacking.do}"
disabled="#{facesContext.postback and not facesContext.validationFailed}">
<f:ajax execute="id1" render="#this id2" listener="#{testBacking.listener}"/>
</h:commandButton>

Any way to bypass validations and still update bean values?

I use Richfaces. I am facing a situation where I have a form and 2 buttons. On click of one of the buttons, say button1, I need the validations. On click of another, button2, I dont. The issue arises when I click on the button1 and I get all validation error messages. This prevents clicking of button2. I tried ajaxSingle, immediate, which help in bypassing the validations but bean values are not updated. How can I achieve that ?
Code :
<h:inputSecret value="#{bean.value} redisplay="true"/>
<a4j:commandButton immediate="true" actionListener="#{bean.actionListener} reRender="ID_OF_PANEL_SURROUNDING_THIS_CODE"/>
Bean :w
public class bean {
.
.
.
public void actionListener(ActionEvent e) {
value = "New value";
}
.
.
.
}
Set immediate attribute to true in your second button.
Immediate attribute skips validation of your values but using immediate for anything other than the trivial case of a cancel button is problematic. If your button2 is some kind of cancel button there is no problem.
If you need to implement something more complex than a cancel button there are some solutions to these problems which you can find in below article.
How_The_Immediate_Attribute_Works
What is actually possible to do for such case is writing a custom validator and do the relevant checks in it.
conditionalValidation.xhtml:
Please notice the h:inputHidden that agregate ids and trigger custom validator to be invoked.
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
template="/WEB-INF/template/default.xhtml">
<ui:param name="title" value="Conditional Validation"/>
<ui:param name="bean" value="#{conditionalValidationBean}"/>
<ui:define name="content">
<a4j:form id="frmConditionalValidation">
<h:panelGrid columns="2">
<h:outputLabel for="txtValue1" value="Value 1"/>
<h:panelGroup>
<h:inputText id="txtValue1" value="#{bean.value1}"/>
<h:message for="txtValue1"/>
</h:panelGroup>
<h:outputLabel for="txtValue2" value="Value 2"/>
<h:panelGroup>
<h:inputText id="txtValue2" value="#{bean.value2}"/>
<h:message for="txtValue2"/>
</h:panelGroup>
</h:panelGrid>
<h:inputHidden id="inpDetailsProvider" required="true"
validator="#{bean.conditionalValidator}" value="1">
<f:attribute name="forceValidationBtnId" value="btnForceValidation"/>
<f:attribute name="value1InputId" value="txtValue1"/>
<f:attribute name="value2InputId" value="txtValue2"/>
</h:inputHidden>
<h:commandButton id="btnForceValidation" value="Force Validation"/>
<h:commandButton id="btnByPassValidation" value="Bypass Validation"/>
</a4j:form>
</ui:define>
</ui:composition>
ConditionalValidationBean.java
public class ConditionalValidationBean {
private String value1;
private String value2;
#SuppressWarnings("unused")
public void conditionalValidator(FacesContext context, UIComponent component, Object value) {
String forceValidationBtnId = (String) component.getAttributes().get("forceValidationBtnId");
String value1InputId = (String) component.getAttributes().get("value1InputId");
String value2InputId = (String) component.getAttributes().get("value2InputId");
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
UICommand forceValidationBtn = findComponent(viewRoot, forceValidationBtnId, UICommand.class);
Map<String, String> params = context.getExternalContext().getRequestParameterMap();
boolean forceValidation = params.containsKey(forceValidationBtn.getClientId(context));
if(forceValidation) {
UIInput value1Input = findComponent(viewRoot, value1InputId, UIInput.class);
UIInput value2Input = findComponent(viewRoot, value2InputId, UIInput.class);
String v1 = (String) value1Input.getValue();
if(v1 == null || v1.trim() == "") {
value1Input.setValid(false);
FacesMessage valueRequiredMsg = new FacesMessage("Value required");
context.addMessage(value1Input.getClientId(context), valueRequiredMsg);
}
// Rest of validation logic.
}
}
public static <T> T findComponent(UIComponent base, String id, Class<T> returnType) {
if (id.equals(base.getId())) {
return returnType.cast(base);
}
Iterator<UIComponent> children = base.getFacetsAndChildren();
while (children.hasNext()) {
T found = findComponent(children.next(), id, returnType);
if (found != null) {
return returnType.cast(found);
}
}
return null;
}
public String getValue1() {
return value1;
}
public String getValue2() {
return value2;
}
public void setValue1(String value1) {
this.value1 = value1;
}
public void setValue2(String value2) {
this.value2 = value2;
}
}

Resources