I have a conceptual mind-block around the use of a JSF 2.0 composite component within a parent page. I understand how to implement an ActionListener (and others) for a composite component's action but how is this wired-up for the parental page to consume? For example, I want my login composite component to perform authentication and when complete, inform the parent page's backing bean through the an event (ActionListener?) to do some UI initialization work. The key here is the login component would say, "Hey, I'm done and the user is good. Your turn."
Thanks in advance for the assistance.
Peace.
Chris
One way I found you can acomplish this is using composite component + custom component type + ActionSource2 + system events.
In the interface of your composite set a component-type (when not definied, the implementation (Mojarra or MyFaces) uses a default component type.
<cc:interface componentType="example.Login">
<cc:attribute name="text" type="java.lang.String"/>
<cc:attribute name="actionExpression" method-signature="void method()"/>
</cc:interface>
<cc:implementation>
<p>
<h:outputLabel value="User"/>
<h:inputText id="user"/>
</p>
<p>
<h:outputLabel value="Password"/>
<h:inputSecret id="password"/>
</p>
</cc:implementation>
This component type is a java class that implements NamingContainer (UINamingContainer is a subclass of component implementing this interface). Next you have to implement ActionSource2 so you could generate an action event when the user is verified.
The verifyng must be after the user and password components have been validated (not your verification but JSF PROCESS VALIDATIONS). In order to know when the validation has occurs, we use System Events.
This is an example of the code for the custom component. The class implements the methods of the ActionSource2 interface and overrides broadcast to handle ActionEvent's. I use some specific classes in Mojarra (because the legacy between ActionSource and ActionSource2).
#FacesComponent("example.Login") //Component type in the composite
#ListenerFor(systemEventClass=PostValidateEvent.class) //Event to listen for user and password verification
public class LoginComponent extends UINamingContainer implements ActionSource2{
#Override
public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
if(event instanceof PostValidateEvent){
System.out.println("post validate");
}
super.processEvent(event);
String user=(String) ((HtmlInputText)findComponent("user")).getValue();
String password=(String) ((HtmlInputSecret)findComponent("password")).getValue();
System.out.println("user: "+user);
System.out.println("password: "+password);
//a simple logic for verification
if(user != null && user.equals("victor") && password != null && password.equals(user)){
System.out.println("user ok");
queueEvent(new ActionEvent(this));
}
}
private MethodExpression exp;
#Override
public MethodExpression getActionExpression() {
return exp;
}
#Override
public void setActionExpression(MethodExpression action) {
exp=action;
}
#Override
public MethodBinding getAction() {
return exp != null ? new MethodBindingMethodExpressionAdapter(exp): null;
}
#Override
public void setAction(MethodBinding action) {
setActionExpression(new MethodExpressionMethodBindingAdapter(action));
}
private MethodBinding actionListener;
#Override
public MethodBinding getActionListener() {
return actionListener;
}
#Override
public void setActionListener(MethodBinding actionListener) {
this.actionListener=actionListener;
}
private boolean i;
#Override
public boolean isImmediate() {
return i;
}
#Override
public void setImmediate(boolean immediate) {
this.i=immediate;
}
List<ActionListener> listeners=new LinkedList<ActionListener>();
#Override
public void addActionListener(ActionListener listener) {
listeners.add(listener);
}
#Override
public ActionListener[] getActionListeners() {
return listeners.toArray(new ActionListener[0]);
}
#Override
public void removeActionListener(ActionListener listener) {
listeners.remove(listener);
}
#Override
public void broadcast(FacesEvent event) throws AbortProcessingException {
super.broadcast(event);
if (event instanceof ActionEvent) {
FacesContext context = getFacesContext();
MethodBinding binding = getActionListener();
if (binding != null) {
binding.invoke(context, new Object[] { event });
}
ActionListener listener = context.getApplication().getActionListener();
if (listener != null) {
listener.processAction((ActionEvent) event);
}
}
}
}
And this is the code in the using page:
<ez:login actionExpression="#{bean.logged}"/>
Related
I'm trying to use primefaces selectManyCheckBox with ajax and converter, but the ajax not fired. If I'm didn't use converter, the ajax can fired. Is there something wrong with my converter?
<div class="form-group">
<p:outputLabel value="Atur Grade Pinjaman" for="gradePinjaman"/>
<p:selectManyCheckbox id="gradePinjaman" value="#{autoInvestController.param.grades}" converter="companyGradeConverter">
<f:selectItems value="#{autoInvestController.grades}" var="grade" itemLabel="#{grade.id}" itemValue="#{grade}"/>
<p:ajax update="selectAll estimation" listener="#{autoInvestController.valueChange}"/>
</p:selectManyCheckbox>
<p:message for="gradePinjaman"/>
</div>
Here is my backing bean code
public void valueChange() {
if (param.getGrades() != null && !param.getGrades().isEmpty()) {
checkAll = param.getGrades().size() == grades.size();
calculateCollectEstimation();
}
}
Here is my converter
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (Strings.isNullOrEmpty(value)) {
return null;
} else {
try {
PlatformService platformService = (PlatformService) CDI.current().select(PlatformService.class).get();
Map<String, Object> param = new HashMap<>();
param.put("id", value);
CompanyGradeResponse companyGrade = platformService.getCompanyGrade(param).get(0);
return companyGrade;
} catch (EndpointException e) {
LOG.error(e.getMessage(), e);
return null;
}
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value != null) {
return ((CompanyGradeResponse) value).getId();
} else {
return null;
}
}
I think my converter work well, but why the ajax won't fired when i check the checkbox?
Thanks
I'm trying to validate the information, user is giving at the registration. One of the fields contains the mailadress, which should be validated by looking in the database to confirm, it doesn't exist yet.
Problem is, that if i type an existing mailadress, it will give back a NonUniqueResultException, but also does store the new user with the duplicate mailadress in the database. Don't understand this, beacuse in the JSF-lifecycle after validation fails, it shouldn't go on to the invoke application phase, right?
Here's my code:
mail field in register formular
<b:inputText id="mail" required="true"
requiredMessage="Bitte geben Sie Ihre E-Mail-Adresse an!"
label="E-Mail" placeholder="name#example.com" value="#{registrierenManagedBean.nutzer.mail}">
<f:validator validatorId="mailValidatorRegistrieren"/>
<b:messages for="mail"/>
</b:inputText>
my custom validator
#FacesValidator("mailValidatorRegistrieren")
public class MailValidatorRegistrieren implements Validator {
#EJB
private DAO dao;
private String mail;
private static final Pattern EMAIL_PATTERN =
Pattern.compile("^[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);
#Override
public void validate(FacesContext facesContext, UIComponent uiComponent, Object o) throws ValidatorException {
mail = (String)o;
boolean matchesPattern = EMAIL_PATTERN.matcher(mail).find();
if(!matchesPattern)
{
throw new ValidatorException((new FacesMessage("Invalid mail")));
}
if(mail.isEmpty()) {
return;
} else if(validateNutzer(mail)){
throw new ValidatorException(new FacesMessage("mail alredy used"));
} else{
return;
}
}
private boolean validateNutzer(String mail) {
try {
Nutzer n = dao.findNutzerByMail(mail);
return n.getMail().equals(mail);
} catch (NullPointerException e) {
return false;
}
}
}
and the "findNutzerByMail"-method from my DAO
public Nutzer findNutzerByMail(String mail) {
try {
return em.createNamedQuery("findNutzerByMail", Nutzer.class)
.setParameter("mail", mail)
.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
I have a Wicket component which is listening for some event (IEvent). If such event arrives, I want to re-render the component with a changed model. There are no active controls on the page, like AjaxLink, which can trigger the re-rendering.
Is there a way to refresh such kind of component?
I was thinking to somehow trigger an AJAX request from the onEvent method and add an AjaxBehavior to the mentioned component. But I don't know, how to trigger the AJAX request.
public class PersonPanel extends Panel implements Observer {
private WebMarkupContainer wrapper;
public PersonPanel(String id) {
super(id);
setDefaultModel(new CompoundPropertyModel<PersonInfo>(getModel()));
wrapper = new WebMarkupContainer("wrapper");
wrapper.setOutputMarkupId(true);
add(wrapper);
wrapper.add(new Label("personID"));
// some more content
}
private IModel<PersonInfo> getModel() {
return new LoadableDetachableModel<PersonInfo>() {
#Override
protected PersonInfo load() {
// model loading logic
}
};
}
#Override
public void onEvent(IEvent<?> event) {
logger.debug("\n Person Panel received an Event: " + event.getPayload());
// Re-rendering of "wrapper" should be triggered from here.
}
#Override
public void update(Observable observable, Object o) {
send(this, Broadcast.EXACT, "Observable cache has changed.");
}
}
Here is the solution, thanks to hint from martin-g, solved via WebSockets. See the methods update and onEvent, plus added WebSocketBehavior on the component:
public class PersonPanel extends Panel implements Observer {
private WebMarkupContainer wrapper;
public PersonPanel(String id) {
super(id);
setDefaultModel(new CompoundPropertyModel<PersonInfo>(getModel()));
wrapper = new WebMarkupContainer("wrapper");
wrapper.setOutputMarkupId(true);
add(wrapper);
wrapper.add(new Label("personID"));
// some more content
add(new WebSocketBehavior() {
});
observableCache.addObserver(this);
}
private IModel<PersonInfo> getModel() {
return new LoadableDetachableModel<PersonInfo>() {
#Override
protected PersonInfo load() {
// model loading logic
}
};
}
#Override
public void onEvent(IEvent<?> event) {
if (event.getPayload() instanceof WebSocketPushPayload) {
WebSocketPushPayload wsEvent = (WebSocketPushPayload) event.getPayload();
wsEvent.getHandler().add(wrapper);
}
}
#Override
public void update(Observable observable, Object o) {
WebSocketSettings webSocketSettings =
WebSocketSettings.Holder.get(getApplication());
WebSocketPushBroadcaster broadcaster =
new WebSocketPushBroadcaster(webSocketSettings.getConnectionRegistry());
broadcaster.broadcastAll(
getApplication(),
new WebSocketMessage("WebSocket message from the PersonPanel."));
}
}
You can find a full running example project, implemented in Wicket 8 and Gradle on Bitbucket:
sw-samuraj/blog-wicket-spring-rest
At the send side you can pass the AjaxRequestTarget with the payload of the event.
send(getPage(), Broadcast.DEPTH, new MyPayload(target));
and then on the receive side:
MyPayload payload = (MyPayload) event.getPayload();
payload.getTarget().add(this);
I have a requirement to create a javascript function that when invoked will save all of the valid components on a JSF 2.0 form. Since the complete form will never be valid as a whole I need to figure out a way to run the lifecycle per component so that if the validation phase is successful the model will be updated and eventually saved.
Ideally, this needs to be a single ajax request as iterating over each component with a separate ajax request would be painfully inefficient.
Has anyone solved the problem before? If not could you give me some pointers on possible implementations?
Edit:
Here's what I have that seems to be working well so far:
#ManagedBean(name = "partialAppSaveBean")
#RequestScoped
public class PartialAppSaveBean implements Serializable {
protected static final Logger LOGGER = LoggerFactory.getLogger(PartialAppSaveBean.class);
private static final long serialVersionUID = 1L;
/**
* Save any valid Application values
*
* #param event
*/
public void saveData(AjaxBehaviorEvent event) {
final FacesContext context = FacesContext.getCurrentInstance();
UIForm form = getParentForm(event.getComponent());
Set<VisitHint> hints = EnumSet.of(VisitHint.SKIP_UNRENDERED);
form.visitTree(VisitContext.createVisitContext(context, null, hints), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent component) {
if (component instanceof UIInput) {
UIInput input = (UIInput) component;
input.validate(context.getFacesContext());
if (input.isValid() && input.getValue() != null) {
ValueExpression valueExpression = input.getValueExpression("value");
if (valueExpression != null
&& valueExpression.getExpressionString() != null) {
try {
valueExpression.setValue(context.getFacesContext().getELContext(), input.getValue());
} catch (Exception ex) {
LOGGER.error("Expression [ " + valueExpression.getExpressionString() +
"] value could not be set with value [" + input.getValue() + "]", ex);
}
}
}
}
return VisitResult.ACCEPT;
}
});
//Save data here
}
/**
* Returns the parent form for this UIComponent
*
* #param component
* #return form
*/
private static UIForm getParentForm(UIComponent component) {
while (component.getParent() != null) {
if (component.getParent() instanceof UIForm) {
return (UIForm) component.getParent();
} else {
return getParentForm(component.getParent());
}
}
return null;
}
}
Invoked with something like:
<h:commandButton
id="saveData">
<f:ajax listener="#{partialAppSaveBean.saveData}" execute="#form" immediate="true" onevent="onPartialSave" />
</h:commandButton>
You could use UIComponent#visitTree() on the UIForm.
FacesContext context = FacesContext.getCurrentInstance();
UIForm form = getFormSomehow();
Map<String, Object> validValues = new HashMap<String, Object>();
Set<VisitHint> hints = EnumSet.of(VisitHint.SKIP_UNRENDERED);
form.visitTree(VisitContext.createVisitContext(context, null, hints), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent component) {
if (component instanceof UIInput) {
UIInput input = (UIInput) component;
if (input.isValid()) {
validValues.put(input.getClientId(context.getFacesContext()), input.getValue());
}
}
return VisitResult.ACCEPT;
}
});
the situation is as follows, i have a page to add/update user
in the prerender method depending on a parameter either creating a new object or getting existing one
#Component("user")
#Scope("request")
public class UserBean {
private User userObj;
private boolean editUser;
public String addUser() throws Exception {
if (editUser) {
userService.updateUser(userObj);
} else {
userService.addUser(userObj);
}
return "users?faces-redirect=true";
}
public void preRender(ComponentSystemEvent event) throws Exception {
System.out.println("############ PRERENDER #############");
if (editUser) {
userObj = userService.getUser(userID);
pageTitle = "Updating " + userObj.getName();
buttonTitle = "Save Changes";
} else {
userObj = new User();
pageTitle = "Adding new user";
buttonTitle = "Add User";
}
}
and in the jsf page i call the prerender as:
<f:event id="event1" listener="#{user.preRender}" type="javax.faces.event.PreRenderComponentEvent" />
but when i press the add button which is as follows:
<h:commandButton value="#{user.buttonTitle}" action="#{user.addUser}" style="width: 105px; "/>
i am getting above exception, please advise.
The problem is that your h:commandButton is creating a new request to the server which causes the UserBean to creating a new instance of itself in scope Request.
There are several solutions I can think of.
1)
It seems that you know if your page is in edit mode or not. Then you can get rid of your preRender method and instead get the userObj from database when calling the getter of userObj. Then you can pass to your add method if your page is in edit mode or not. Therefore you have to modify your commandButton: (note: you have to change the value to your current edit mode)
<h:commandButton value="#{user.buttonTitle}" action="#{user.addUser}" style="width: 105px; ">
<f:setPropertyActionListener value="true" target="#{user.editUser}" />
</h:commandButton>
and your userBean to:
#Component("user")
#Scope("request")
public class UserBean {
private User userObj;
private boolean editUser;
public String addUser() throws Exception {
userObj = getUserObj();
if (editUser) {
userService.updateUser(userObj);
} else {
userService.addUser(userObj);
}
return "users?faces-redirect=true";
}
public void setEditUser(boolean editUser) {
this.editUser = editUser;
}
public User getUserObj() {
if (editUser) {
if(userObj == null) {
userObj = userService.getUser(userID);
}
return userObj;
}
else {
return userObj = new User();
}
}
public void setUserObj(User userObj) {
this.userObj = userObj;
}
This should give you a basic idea how it works. The trick is to use the f:setPropertyActionListener.
2) You could use the view scope to solve that issue. The problem is spring doesn't offer this out of the box. The good news is that you can build the view scope on your own. That a look at that blog post.