Composite-Component within a form that requires interaction/validation - java

A quick background : I have put a captcha using the primefaces custom component on my website. People in charge don't like it as it is too difficult to use and clients are/were complaining. I decided I wanted to create a simple component (ie: 4 + 9 = and user inputs the answer) to avoid a bit of spam. No need to have an image display the question, just using simple text. This got me looking into custom components and composite component ( from this article and this one ).
Now, the question is not so much about a makeshift basic "captcha style validation". It's more about a composite component and backing bean combo.
What I would do is create a backing bean in this style :
<cc:interface>
<cc:attribute name="label" />
<!-- edited -->
<cc:attribute name="required" />
<cc:attribute name="ident" />
</cc:interface>
<cc:implementation>
<h:panelGrid columns="3">
<h:outputText value="#{captcha.text}"/>
<h:inputText id="#{cc.attrs.ident}" value="#{captcha.entered}" validator="#{captcha.validate}" required="#{cc.attrs.required eq 'true'}" label="#{cc.attrs.label}" />
<h:message for="captchaAnswer" />
</h:panelGrid>
<h:inputHidden value="#{captcha.value}" />
</cc:implementation>
And then I'd like to use this component in this manner :
<h:form>
...
<tr>
<td>
<my:captcha label="Captcha" ident="captcha" required="true"/> <!-- added after suggested comment -->
<br/>
<h:message for="captcha" class="error"/>
</td>
</tr>
<tr>
<td colspan="3" class="center">
<h:commandButton class="button" value="#{msg['contact.label.send']}" action="#{contact.send}" >
</h:commandButton>
</td>
</tr>
...
</h:form>
How can I make sure that on submit I can check that my {#captcha.entered} value is the required value and if not returns a validation message on the form and prevents it from being submitted?
captcha backing bean would be simple and have values : text, answer, and, entered and a simple function to check if answer == entered.
EDIT : (Attempt #1)
custom validator would look as follow
public void validate(FacesContext context, UIComponent toValidate, Object value) {
System.out.println("validating");
String input = (String) value;
//check to see if input is an integer
//if not we know right away that the value is not good
if(StringUtils.isNumeric(input)) {
//if the input is numeric, convert to an integer
int intValue = new Integer(input).intValue();
if (intValue != answer) {
((UIInput) toValidate).setValid(false);
FacesMessage message = new FacesMessage("Not a match!!");
context.addMessage(toValidate.getClientId(context), message);
}
}
}
In this instance the validator does not even get called and I don't get an error message.
Edit #2
After a bit of working and hints from comments I got this working. To get the h:message working I needed to add the attribute ident instead of id. If not I had to reference it as so : <h:message for="captcha:captcha" /> which was not the desired outcome.

The primary issue with this question was that I couldn't get a reference.
By adding the attribute ident instead of id I got this to work. Please refer to edit #2 in question.

Related

Reset form values onload in bootsfaces

I need to reset the form fields and clear the form values onload.
However I used -
public void refresh() {
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
ViewHandler viewHandler = application.getViewHandler();
UIViewRoot viewRoot = viewHandler.createView(context, context
.getViewRoot().getViewId());
context.setViewRoot(viewRoot);
context.renderResponse(); //Optional
}
The above code is working but when I am entering values in fields using in panelgroup , its not working.
<h:panelGroup>
<ui:repeat value="#{formDynAttrbBean.vals}" var="values">
<b:panelGrid columns="6" cellspacing="4">
<b:inputText id="label" value="">
</b:inputText>
<b:selectOneMenu value="">
<f:selectItem itemLabel="SelectType" itemValue="" />
<f:selectItems value="" />
</b:selectOneMenu>
<b:selectBooleanCheckbox value="" />
<b:commandButton actionListener="#{form.types}" />
</b:panelGrid>
</ui:repeat>
<b:commandButton tooltip="Add new attribute" tooltip-position="bottom"
iconAwesome="plus" actionListener="#{form.add}" update="#form" />
<tr />
<b:commandButton value="Save" ajax="true"
actionListener="#{form.submit}" update="#form" />
<tr />
<b:commandButton type="reset" action="#{formController.refresh}"
value="Reset">
</b:commandButton>
</h:panelGroup>
Refresh function is not working in this case when I am entering values in panelgroup and in repeating fields.
I am just a beginner to bootsfaces.Please suggest me an approach.Thankyou in advance.
There are ways to clear the input fields of the form. For example, JSF 2.2 has the resetValues="true" attribute (see the article of Michael Kurz (probably not implemented by BootsFaces). Another option is to use <b:commandButton type="reset">.
But I wonder why you want to reset the values on the client side? You have to clear them because they display some "dirty" (i.e. modified) backend bean. IMHO the better approach is to create a new, empty backend bean.

JSF - Calling bean method when clicking <a> element

So I have an anchor element, and it's href attribute is set dynamically from the bean. I am using an anchor since it is interacting with fancybox javascript. What I need to have happen is to call a method in the bean when the anchor is clicked so that the href will update again. There is a textbox above that is also bound to bean. What needs to happen is to take the text from the text box, and parse that into a query string for the anchor's href.
I'm trying to figure out the best way to have this happen (if it is possible). Would I be able to use javascript (or Ajax) to call an updateHref() method?
The textbox and anchor element (it's ModelBean.ending that needs to be updated):
<h:inputTextarea value="#{ModelBean.tweet}" class="textarea.hs-input #{ModelBean.tweetClass}" style="width:200px;" >
<f:facet name="label">
Tweet
</f:facet>
</h:inputTextarea>
</td>
</tr>
<tr>
<td>
<h:commandButton value="Analyze Tweet" class="hs-button orange" action="#{ModelBean.tweetAnalysis()}" />
</td>
<td>
<a href="https://app.hubspot.com/social/#{ModelBean.hubID}/publishing/compose_bookmarklet?body=#{ModelBean.ending}" class="hs-button primary fancybox-iframe" data-fancybox-type="iframe" >
Tweet it!
</a>
</td>
</tr>
Here's the bit of java that would need to be called:
ending = URLEncoder.encode(tweet, "UTF-8");
ending = ending.replaceAll("\\+", "%20");
If you think any other part of the code would be useful please let me know! And thanks in advance for any suggestions!
If you use JSF 2.0, f:ajax tag may solve your problem. Here is sample usege of f:ajax tag,
<h:commandLink value="#{..}">
<f:ajax execute="name" render="output" />
</h:commandLink>
<h:inputTextarea id="output" value="#{..}" />
execute=”name” – Indicate the form component with an Id of “name” will be sent to the server for processing. For multiple components, just split it with a space in between, e.g execute=”name anotherId anotherxxId”. In this case, it will submit the text box value.
render=”output” – After the Ajax request, it will refresh the component with an id of “output“. In this case, after the Ajax request is finished, it will refresh the h:inputTextarea component.

Can I add object represented by selectOneMenu to a collection in a bean in JSF?

So I have bean named element that has a list of categories in and I want to add to that list from a drop down box.
There is a submit at the bottom of the page that persists my element and all works fine, I have a converter in place for the drop down which is also working, but I can't work out how to get an object of type Category from my drop down and add it to the category list in my bean.
Here is the section of my JSF I'm trying to achieve this from:
<table>
<tr>
<th class="textRight">Choose Category</th>
<td>
<h:selectOneMenu id="currentCategory">
<f:selectItems value="#{serviceWeb.listCategories()}" />
</h:selectOneMenu>
</td>
<td>
<h:commandButton id="addCategory" value="Add"
action="element.categories.add(#{currentCategory.value})" />
</td>
</tr>
</table>
I know this doesn't work, I get the error:
action="element.categories.add(#{currentCategory.value})" Not a Valid Method Expression
to explain, there is no backing bean for this menu item, I'm trying to get the value from the component itself.
So I guess you can't do it this way, but how do you do it?
Is it possible?
Bind the component to the view (which will in case of <h:selectOneMenu> resolve to an instance of HtmlSelectOneMenu) and use UIInput#getValue() as action method argument and fix your invalid EL syntax.
<h:selectOneMenu binding="#{currentCategory}">
<f:selectItems value="#{serviceWeb.listCategories()}" />
</h:selectOneMenu>
<h:commandButton value="Add" action="#{element.categories.add(currentCategory.value)}" />

Passing Parameters With Seam, RichFaces & PopupPanel

I'm trying to get a little application working using Seam 3, RichFaces 4 and hitting a few troubles with passing some parameters around. I've tried a lot of different things but I keep falling at the final hurdle. I'm carrying through a customerId via a request parameter. But when I hit a RichFaces commandButton on a popupPanel, that customerId is no longer available to me.
I'm setting up an application to manage some data. Basically you select a customer from one screen, that will take you to another screen containing "repositories" where you then can create, edit etc. You can get to this second repositories page via the URL:
http://localhost:8080/media-manager/repositories.xhtml?customer=12
I then have a bean picking this value up:
#Named
#RequestScoped
public class RepositoryBean extends AbstractViewBean<Repository> {
// Various properties etc. here
private Long customerId;
public void init() {
log.info("Customer ID is "+ customerId);
}
}
I then set the customer ID via metadata on the repository page and call init:
<f:metadata>
<f:viewParam name="customer" value="#{repositoryBean.customerId}"/>
<f:event type="preRenderView" listener="#{repositoryBean.init}" />
</f:metadata>
This works well at the start. I can display information I need for the customer with the supplied ID. But when I try to create my popupPanel is goes a bit wrong. Here's a simplified version of the code first:
<rich:popupPanel id="repositoryModalPanel" modal="true" resizeable="true">
<f:facet name="header">Title</f:facet>
<f:facet name="controls">
<h:outputLink value="#" onclick="#{rich:component('repositoryModalPanel')}.hide(); return false;">X</h:outputLink>
</f:facet>
<h:form id="modalForm" class="modalForm">
<fieldset>
<ul class="layout form">
<li>
<label for="name" class="required">Name:</label>
<h:inputText id="name" value="#{repositoryBean.instance.name}" required="true">
<!-- rich:validator event="blur" / -->
</h:inputText>
<rich:message for="name" errorClass="error errormessage" />
</li>
<li class="last">
<a4j:commandButton id="create" value="Create" action="#{repositoryBean.saveRepository}" rendered="#{empty repositoryBean.instance.id}"/>
<a4j:commandButton id="save" value="Save" action="#{repositoryBean.saveRepository}" rendered="#{not empty repositoryBean.instance.id}"/>
<a4j:commandButton id="cancel" value="Cancel" action="#{repositoryBean.clearInstance}" immediate="true" />
</li>
</ul>
</fieldset>
</h:form>
Basically, whenever I hit the save commandButton, the init method is called but the customerId member is never populated. Does anyone have any insight into why?
I've read that viewParam is only for GET requests so maybe that's the problem? But if that's the case - what is the other solution? Lots of things I have seen suggested (e.g. using #ManagedProperty) does not seem to be applicable to Seam 3.
RepositoryBean is RequestScoped, and as such will be newly created on each request, especially if you hit the save button.
The straightforward solution is to promote RepositoryBean to be ConversationScoped, and to make it long running if you enter the page.
#Named
#ConversationScoped
public class RepositoryBean extends AbstractViewBean<Repository> {
// Various properties etc. here
#In
Conversation conversation
private Long customerId;
public void init() {
log.info("Customer ID is "+ customerId);
conversation.begin();
}
}
The easieast way for that is to dump the preRenderView and use seam 3 view-action instead.
<f:metadata>
<f:viewParam name="customer" value="#{repositoryBean.customerId}"/>
<s:viewAction action="#{repositoryBean.init}" if="#{conversation.transient}"/>
</f:metadata>

Dynamic required validation for different buttons?

I'm currently learning about jsf 2.0 from core jsf 2.0 book + glassfish + cdi.
I have a question about the validation feature of JSF.
Let's say i have a very simple login application, which has a very simple layout like :
userid : (input field for userid - using required="true")
password : (input secret for password - using required="true")
loginButton + registerButton(using immediate="true") + checkUserIdAvailabilityButton
Now, let's say the loginButton is pressed, and the userid and password are left empty, validation error would occur on both of the fields, and that's working as what i intended.
And when the registerButton is pressed, it doesnt care whether the userid or password is filled by the user, since it's using immediate="true", thus bypassing validation and the command gets executed at the apply request value phase, and that's still working as what i intended.
And here comes my problem .. When the checkUserIdAvailabilityButton is pressed, i only expect the userid to be filled, and i dont need to care about whether the password field is filled or not, but the password field will throw error saying that it's a required field.
Is there anyway to resolve this kind of problem ? I know this could be a very simple application, but in my working place, i think they design lots of screens like this, like the save button along with refresh button with different required fields but the buttons are in the same page.
Thank you !
Set both the availability button and username field with immediate="true" and put the register button in a separate <h:form>.
E.g.
<h:form>
<h:inputText value="#{bean.username}" required="true" immediate="true" />
<h:inputSecret value="#{bean.password}" required="true" />
<h:commandButton value="Login" action="#{bean.login}" />
<h:commandButton value="Check name availability" action="#{bean.checkNameAvailability}" immediate="true" />
</h:form>
<h:form>
<h:commandButton value="Register" action="#{bean.register}" />
</h:form>
An alternative is to determine in the required attribute which button is been pressed (it's then present as request parameter).
<h:form id="form">
<h:inputText value="#{bean.username}" required="#{not empty param['form:login'] or not empty param['form:check']}" />
<h:inputSecret value="#{bean.password}" required="#{not empty param['form:login']}" />
<h:commandButton id="login" value="Login" action="#{bean.login}" />
<h:commandButton id="check" value="Check name availability" action="#{bean.checkNameAvailability}" />
<h:commandButton value="Register" action="#{bean.register}" />
</h:form>

Categories