Implement callback method in JSF composite component - jsf-2.2

I'd like to implement a "callback" feature in my composite component. This method would be called from the backing component when necessary.
The XHTML part is:
<cc:interface componentType="partnerSelComp">
<cc:attribute name="value" type="com.app.data.Partner"/>
<cc:attribute name="callback" method-signature="void notify(java.lang.Long)"/>
</cc:interface>
<cc:implementation>
<span id="#{cc.clientId}">
<p:inputText id="code" binding="#{cc.partnerCode}">
<p:ajax event="blur" update="code name msg" listener="#{cc.validate}" />
</p:inputText>
<p:outputLabel id ="name" binding="#{cc.partnerName}"/>
<p:message id="msg" for="code"/>
</span>
</cc:implementation>
The relevant part of backing component is:
#FacesComponent("partnerSelComp")
public class PartnerSelComp extends UIInput implements NamingContainer {
private InputText partnerCode;
private OutputLabel partnerName;
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
public void validate() {
...
MethodExpression exp = (MethodExpression) getAttributes().get("callback");
...
}
...
}
Above the exp gets null value. How can I get the callback attribute, and how can I execute it?

A solution (Java 1.8) to call a function with no args.
Component:
<composite:interface>
<composite:attribute name="buttonCallback" type="java.util.function.Consumer"/>
</composite:interface>
<composite:implementation>
<p:commandButton value="Submit" action="#{cc.attrs.buttonCallback.accept(null)}"/>
<composite:implementation>
Usage in view:
<mycomp:myCC buttonCallback="#{myBackingBean.myCallback}"/>
Usage in backing bean:
private Consumer<Void> myCallback;
#PostConstruct
public void init() {
myCallback= this::myFunction;
}
public void myFunction(Void v) { //... }

Related

p:commandButton update action won't clear input fields after submission

<h:form id="registrationForm">
UserName:
<p:inputText id="usernameInput" value="#{userUI.thisUsername}" />
<br />
Password:
<p:inputText id="passwordInput" value="#{userUI.thisPassword}" />
<br />
<p:commandButton actionListener="#{userUI.createUser}"
update="registrationForm" value="Create" />
</h:form>
I want this form to reset its values after submission.
I enter the username and password, hit submit, it resets the values and persists empty values, not the ones i entered. Tried moving the commandButton out of h:form but it didn't change a thing. Also tried adding
<p:ajax update="registrationForm" resetValues="true" />
It still persisted empty values.
How do i do it right?
Give this a try:
Bean is of scoped Request.
To reset values reinitialize fields in post constructor, or call it manually in your createUser method.
#ManagedBean
#RequestScoped
public class UserUI implements Serializable {
private static final long serialVersionUID = 1L;
private String thisUsername;
private String thisPassword;
public UserUI(){
}
#PostConstruct
public void init(){
thisUsername="";
thisPassword="";
}
public void createUser(){
System.out.println("ThisUserName :"+thisUsername);
System.out.println("thisPassword :"+thisPassword);
init();
}
/////////GETTER SETTER
//thisUsername
//thisPassword
}
Your webPage.xhtml will be look like:
<h:form id="registrationForm">
<p:panelGrid columns="2">
<h:outputLabel value="UserName:" for="usernameInput" />
<p:inputText id="usernameInput" value="#{userUI.thisUsername}" />
<h:outputLabel value="Password:" for="passwordInput" />
<p:inputText id="passwordInput" value="#{userUI.thisPassword}" />
<p/>
<p:commandButton action="#{userUI.createUser}" update="#form" value="Create" />
</p:panelGrid>
</h:form>

Ajax keyup event doesn't work after render

I have problem with JSF and Ajax. I used Ajax keyup event to filter selectOneListbox. It's working fine if I render all on start. When I hide panel with this component and render it when I select appropriate option in selectOneRadio, keyup event doesn't work.
JSF code:
<h:form id="form">
Client type: <h:selectOneRadio id="client_type" value="#{testBean.clientType}" styleClass="radio">
<f:selectItem itemValue="N" itemLabel="New client" />
<f:selectItem itemValue="E" itemLabel="Existing client" />
<f:ajax event="change" execute="#this" render="clientPanel" />
</h:selectOneRadio>
<br /><br />
<h:panelGroup id="clientPanel">
<h:panelGroup id="existedClient" rendered="#{testBean.clientType eq 'E'}">
<p style="font-weight: bold;">Existed client</p>
Search: <h:inputText id="in" value="#{testBean.clientSearchPattern}" autocomplete="off">
<f:ajax event="keyup" listener="#{testBean.clientChanged}" render="client_select" />
</h:inputText>
Client: <h:selectOneListbox id="client_select" value="#{testBean.complaint.client.id}" required="true"
style="width: 200px; font-family: 'Ubuntu'; font-size: 14.6667px; font-style: normal;">
<f:selectItems value="#{testBean.clients}" var="client"
itemLabel="#{client.name} #{client.surname}" itemValue="#{client.id}" />
</h:selectOneListbox>
</h:panelGroup>
<br /><br />
<h:panelGroup id="newClient" rendered="#{testBean.clientType eq 'N'}">
<p style="font-weight: bold;">New client</p>
Name: <h:inputText id="name" value="#{testBean.clientSearchPattern}" />
</h:panelGroup>
</h:panelGroup>
</h:form>
Java code:
#Named
#RequestScoped
public class TestBean {
#Inject
private ClientService clientService;
private String clientType = "N";
private String clientSearchPattern;
private List<Client> clients;
private Complaint complaint = new Complaint();
#PostConstruct
public void init() {
clients = clientService.list();
}
public void clientChanged() {
clients = clientService.search(clientSearchPattern);
}
public String getClientSearchPattern() {
return clientSearchPattern;
}
public void setClientSearchPattern(String clientSearchPattern) {
this.clientSearchPattern = clientSearchPattern;
}
public List<Client> getClients() {
return clients;
}
public Complaint getComplaint() {
return complaint;
}
public void setComplaint(Complaint complaint) {
this.complaint = complaint;
}
public String getClientType() {
return clientType;
}
public void setClientType(String clientType) {
this.clientType = clientType;
}
}
Thanks in advance!

capture selected text from inputTextArea primefaces jsf

I want to capture selected text from inputTextArea on ajax dblclick or select event.How can I do this ?
The code below selects everything in the text area (which I don't want). Thank you very much.
<h:form id="form">
<p:panel header="Select Text">
<h:panelGrid columns="3" cellpadding="5">
<h:outputText value="Text: " />
<p:inputTextarea id="textarea"
value="#{selectedTextBean.selectedText}">
<p:ajax event="select" update="selectedText" />
</p:inputTextarea>
<h:outputText id="selectedText"
value="#{selectedTextBean.selectedText}" />
</h:panelGrid>
</p:panel>
</h:form>
Here is SelectedTextBean
#ManagedBean
#ViewScoped
public class SelectedTextBean {
public SelectedTextBean() {
}
private String selectedText;
public String getSelectedText() {
return selectedText;
}
public void setSelectedText(String selectedText) {
this.selectedText = selectedText;
}
}
You can use this plugin jquery-textrange.
xhtml
<p:inputTextarea onselect="setSelectedText()" />
<p:remoteCommand name="setSelectedTextCommand"
actionListener="#{mainBean.setSelectedText()}"
update="currentSelectedText" />
Selected Text is:
<h:outputText value="#{mainBean.selectedTextInArea}"
id="currentSelectedText" />
<h:outputScript library="js" name="jquery-textrange.js" />
<script>
function setSelectedText() {
var range = $('.ui-inputtextarea').textrange();// general selector
setSelectedTextCommand([{name: 'selectedText', value: range.text}]);
}
</script>
Bean
private String selectedTextInArea;
public void setSelectedText() {
FacesContext context = FacesContext.getCurrentInstance();
Map map = context.getExternalContext().getRequestParameterMap();
selectedTextInArea = (String) map.get("selectedText");
}
public String getSelectedTextInArea() {
return selectedTextInArea;
}
public void setSelectedTextInArea(String selectedTextInArea) {
this.selectedTextInArea = selectedTextInArea;
}
And Here's a live demo on Primefaces TextArea Selection, and on github.
You can do that sending a parameter to a remote command as follows:
The View
<h:form id="form">
<p:panel header="Select Text">
<h:panelGrid columns="3" cellpadding="5">
<h:outputText value="Text: " />
<h:panelGroup>
<p:inputTextarea id="textarea"
value="#{selectedTextBean.selectedText}" onselect="processSelection();" />
<p:remoteCommand name="sendSelection" actionListener="#{selectedTextBean.onSelect}" update="selectedText" process="#this" />
</h:panelGroup>
<h:outputText id="selectedText"
value="#{selectedTextBean.selectedText}" />
</h:panelGrid>
</p:panel>
</h:form>
<script>
function processSelection() {
var selectedText = (!!document.getSelection) ? document.getSelection() :
(!!window.getSelection) ? window.getSelection() :
document.selection.createRange().text;
sendSelection([{name: 'selectedText', value: selectedText}]);
}
</script>
Note that the text selection changes depending on the browser.
The Bean
import java.io.Serializable;
import java.util.Map;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
#ManagedBean
#ViewScoped
public class SelectedTextBean implements Serializable {
public SelectedTextBean() {
}
private String selectedText;
public void onSelect() {
FacesContext context = FacesContext.getCurrentInstance();
Map map = context.getExternalContext().getRequestParameterMap();
selectedText = (String) map.get("selectedText");
}
public String getSelectedText() {
return selectedText;
}
public void setSelectedText(String selectedText) {
this.selectedText = selectedText;
}
}

Primefaces ajax pagination not working

I have following jsf page, in which datatable is populated via ajax request from database. Problem is, after pushing another page button on pagination bar, table shows no results. Changing scope of backing bean to session helps, but it is not a solution. Why it is happening?
<h:form>
<h:panelGrid columns="2" cellpadding="8" style="width: 545px">
<h:panelGroup>
<p:outputLabel value="Client name: " for="searchString" />
<br />
<p:inputText id="searchString" title="searchString" value="#{findClientBean.searchString}" />
</h:panelGroup>
<h:panelGroup>
<br />
<p:message for="searchString" />
</h:panelGroup>
<p:commandButton value="Search" styleClass="pCommandButton" >
<f:ajax execute="searchString" listener="#{findClientBean.findClient}" render=":resultTable" />
</p:commandButton>
</h:panelGrid>
</h:form>
<br />
<p:dataTable id="resultTable" var="client" value="#{findClientBean.resultList}" paginator="true" rows="10"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="5,10,15" paginatorPosition="bottom">
<p:column headerText="Search results">
<h:outputLink value="../temp.xhtml?id=#{client.id}">#{client.firstName} #{client.lastName}</h:outputLink>
</p:column>
</p:dataTable>
<h:form>
Backing bean code:
#Named
#RequestScoped
public class FindClientBean implements Serializable {
#Inject
private ClientDAO clientDAO;
#NotNull(message="Search string cannot be empty")
private String searchString;
private List<Client> resultList;
public void findClient() {
resultList = clientDAO.findClientByNameOrLastnamePart(searchString);
}
public void setResultList(List<Client> resultList) {
this.resultList = resultList;
}
public List<Client> getResultList() {
return resultList;
}
public String getSearchString() {
return searchString;
}
public void setSearchString(String searchString) {
this.searchString = searchString;
}
}
Your Client resultList is being populated whenever the user presses the Search button.
As the collection is inside a RequestScoped bean, the resultList will be erased as soon as it is sent back to the View (along with the entire bean).
As a result, when the user tries to navigate to another page (thus making a second request) the component won't find a populated resultList anymore and a "No records found" message will be displayed.
"Promote" you bean to ViewScoped (or any scope that would make your bean live longer).
#Named
#ViewScoped
public class FindClientBean implements Serializable{
(...)
}

JSF validate on submit form

I'm working on a JSF 2.0 form, I have a managedbean with 2 fields
import java.util.Date;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class StackOverflow {
private Date firstDate;
private Date secondDate;
public void submit(){
//validate here and show error on form
}
}
and the xhtml like:
<h:inputText value="#{stackOverflow.firstDate}">
<f:convertDateTime pattern="d/M/yyyy" />
</h:inputText>
<h:inputText value="#{stackOverflow.secondDate}">
<f:convertDateTime pattern="d/M/yyyy" />
</h:inputText>
<h:commandLink action="#{stackOverflow.submit}">
<span>Submit</span>
</h:commandLink>
I want to validate the first and second date that the second date is not before the first date
Here's one of the ways:
<h:messages globalOnly="true"/>
<h:form>
<h:inputText value="#{stackOverflow.firstDate}" binding="#{firstDate}">
<f:convertDateTime pattern="d/M/yyyy" />
</h:inputText>
<h:inputText value="#{stackOverflow.secondDate}" validator="dateValidator">
<f:attribute name="firstDate" value="#{firstDate}" />
<f:convertDateTime pattern="d/M/yyyy" />
</h:inputText>
<h:commandButton value="Submit" action="#{stackOverflow.submit}"/>
</h:form>
with
#FacesValidator(value="dateValidator")
public class DateValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
UIInput sd = (UIInput)component.getAttributes().get("firstDate");
Date firstDate = (Date)sd.getValue();
Date secondDate = (Date)value;
if(!firstDate.before(secondDate)){
FacesMessage msg = new FacesMessage("Entered dates are invalid: first date must be before second date");
throw new ValidatorException(msg);
}
}
}

Resources