How to extend AjaxBehaviorEvent dispatched from #FacesComponent? - jsf

When I dispatch an ajax event from the Composite Component by using <cc:clientBehavior name="chartUpdated" event="change" targets="chartdata"/> I catch it in Facelet page by using <f:ajax event="chartUpdated" listener="#{bean.updateListener}">. And In backing bean I capture event of type AjaxBehaviorEvent.
public void updateListener(AjaxBehaviorEvent event){
...
}
I undertand that I can extend AjaxBehaviorEvent and pass within it object which has been changed. For example, Primefaces's Scheduler uses this approach:
<p:ajax event="eventMove" listener="#{scheduleView.onEventMove}" update="messages" />
And backing bean:
public void onEventMove(ScheduleEntryMoveEvent event) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Event moved", "Day delta:" + event.getDayDelta() + ", Minute delta:" + event.getMinuteDelta());
addMessage(message);
}
Is it possible to achieve the same functionality by using Composite Component together with the #FacesComponent ?
Thank you in advance!

Nice to meet you, again :)
continuing from your previous question:
Override queueEvent() to filter interesting events (changes from specific components) and postpone their enqueue to validation phase to be able to fetch converted & validated values:
#FacesComponent("rangeComponent")
public class RangeComponent extends UIInput implements NamingContainer
{
private final List<AjaxBehaviorEvent> customEvents = new ArrayList<>();
...
#Override
public void queueEvent(FacesEvent event)
{
FacesContext context = getFacesContext();
if(event instanceof AjaxBehaviorEvent)
{
Map<String, String> params = context.getExternalContext().getRequestParameterMap();
String eventName = params.get("javax.faces.behavior.event");
Object eventSource = event.getSource();
if("change".equals(eventName) && (from.equals(eventSource) || to.equals(eventSource)))
{
customEvents.add((AjaxBehaviorEvent) event);
return;
}
}
super.queueEvent(event);
}
#Override
public void validate(FacesContext context)
{
super.validate(context);
if(from.isValid() && to.isValid())
{
for(AjaxBehaviorEvent event : customEvents)
{
SelectEvent selectEvent = new SelectEvent(this, event.getBehavior(), this.getValue());
if(event.getPhaseId().equals(PhaseId.APPLY_REQUEST_VALUES))
{
selectEvent.setPhaseId(PhaseId.PROCESS_VALIDATIONS);
}
else
{
selectEvent.setPhaseId(PhaseId.INVOKE_APPLICATION);
}
super.queueEvent(selectEvent);
}
}
}
...
}
then add the specific event listener to your managed bean:
#ManagedBean
#ViewScoped
public class RangeBean implements Serializable
{
private static final long serialVersionUID = 1L;
private String range = "01/01/2015-31/12/2015";
public void onSelect(SelectEvent event)
{
Messages.addGlobalInfo("[{0}] selected: [{1}]", event.getComponent().getId(), event.getObject());
}
public String getRange()
{
return range;
}
public void setRange(String range)
{
this.range = range;
}
}

Related

o:socket in EJB class - I get a NPE in org.omnifaces.util.Messages

I´m trying to send a push notification from my EJB class:
#Stateless
public class SendEmailNotificationReminderServiceBean implements SendEmailNotificationReminderService {
#Inject
private BeanManager beanManager;
private void sendNotification {
// Push Nachricht
PushEvent event = new PushEvent("sendNotification", 1);
beanManager.fireEvent(event);
}
}
Here my socket:
#Named
#ApplicationScoped
public class NotificationSocket implements Serializable {
private static final long serialVersionUID = 7615791377170410627L;
#Inject
#Push(channel = "notificationChannel")
private PushContext push;
/**
* Push Notification
*
* #param recipientUser
*/
public void pushUser(#Observes PushEvent event) {
if (event == null)
return;
Set<Future<Void>> sent = push.send(event.getMessage(), event.getUserId());
}
}
My object:
public class PushEvent {
private String message;
private Long userId;
public PushEvent(String message) {
super();
this.message = message;
}
//////////////
public PushEvent(String message, Long userId) {
super();
this.message = message;
this.userId = userId;
}
public String getMessage() {
return message;
}
public Long getUserId() {
return userId;
}
}
My JSF page:
<o:socket channel="notificationChannel"
user="#{loginBean.currentEmployee.id}" scope="session"
onmessage="notificationLoadScript">
</o:socket>
<h:form id="notificationPushTopbarForm">
<p:remoteCommand name="notificationLoadScript"
actionListener="#{topbarMenuController.loadNotification()}"
oncomplete="changeTitleForNotification(#{topbarMenuController.numberOfNewNotificationAvailable})"
update=":notificationLink, :notificationSidebarForm" global="false" />
</h:form>
What I´m trying to do is:
Create a PushNotification from my backend (EJB layer) to the current logged in User and this channel.
As example you can imagine the Notification icon (right corner) from Stackoverflow.
I´m getting no error message, but the JSF component is also not updating (p:remoteCommand call). Any idea how I can fix this?

Initialise List based on a JPA query?

Good afternoon. I have a list of operators which I initialize as follows.
#ManagedBean
#ViewScoped
public class TiemposBean implements Serializable {
#EJB
private OperariosFacade operariosFacade;
private List<Operarios> operariosList;
#PostConstruct
public void inicializarBean() {
operariosList = operariosFacade.findAll();
}
public List<Operarios> getOperariosList() {
return operariosList;
}
public void setOperariosList(List<Operarios> operariosList) {
this.operariosList = operariosList;
}
}
The list is initialized normally and I use it without any problem but I want to initialize it based on the following database query.
SELECT * FROM `operarios` WHERE `ESTADO_OPERARIO` = 1 AND `TIPO_ESTADO_OPERARIO` = 1;
In my JPQL query is as follows.
#NamedQuery(name = "Operarios.findByNombreLista", query = "SELECT o FROM Operarios o WHERE o.idEstadoOperario = :idEstadoOperario AND o.tipoEstadoOperario = :tipoEstadoOperario"),
And the method I built to invoke that query is as follows.
public void inicializarLista() {
String namedQuery = "Operarios.findByNombreLista";
Map<String, Object> parametros = new HashMap<>();
parametros.put("idEstadoOperario", 1);
parametros.put("tipoEstadoOperario", 1);
operariosList = operariosFacade.findByNamedQuery(namedQuery, parametros);
}
and there's modified my bean.
#ManagedBean
#ViewScoped
public class TiemposBean implements Serializable {
#EJB
private OperariosFacade operariosFacade;
private List<Operarios> operariosList;
#PostConstruct
public void inicializarBean() {
operariosList = new ArrayList<>();
}
public List<Operarios> getOperariosList() {
return operariosList;
}
public void setOperariosList(List<Operarios> operariosList) {
this.operariosList = operariosList;
}
}
and in my view I call it as follows
<p:outputLabel for="somOperario" value="Operario"/>
<p:selectOneMenu id="somOperario" value="#{tiemposBean.operarioSeleccionado}"
filter="true"
converter="operariosConverter">
<f:selectItem itemLabel="Seleccione uno"/>
<f:selectItems value="#{tiemposBean.operariosList}"
var="operarioVar"
itemValue="#{operarioVar}"
itemLabel="#{operarioVar.nombre}"
/>
</p:selectOneMenu>
<p:message for="somOperario" id="msg_somOperario"/>
But I don't initialize it to me that I am doing wrong please I need your help. thanks
Based on the code that you posted, the "inicializarLista" method is not called. You have a managed bean with:
#PostConstruct
public void inicializarBean() {
operariosList = new ArrayList<>();
}
But you are loading your list in:
public void inicializarLista()
Please, call the inicializarLista() inside #PostConstruct method:
#PostConstruct
public void inicializarBean() {
//This step is not necessary any more!
operariosList = new ArrayList<>();
inicializarLista();
}

Custom Composite Component can read but can't retrieve submited value

This is how I'm rendering my composite component inside a loop, it works, but when I switch to edit mode and sumbmit new values I can't retrieve them from the InputText.
#FacesComponent("customComponent")
public class CustomComponent extends UIInput implements NamingContainer, Serializable {
private static final long serialVersionUID = 1L;
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
private UIComponent component;
private HtmlInputText inputTextValue;
#Override
public void encodeBegin(FacesContext context) throws IOException {
AttributeObject attrObject = (AttributeObject) getAttributes().get("value");
Boolean enableInput = (Boolean) getAttributes().get("enableInput");
if (attrObject.getAttributeValue() != null) {
if (attrObject.getAttributeDescriptor().getDataType() == DataTypeConstants.TEXT && enableInput) {
InputText inputText = new InputText();
inputText.setRequired(true);
inputText.setValueExpression("binding",
createValueExpression("#{searchController.myComponent}", UIComponent.class));
inputText.setId("editableTextId");
inputText.encodeAll(context);
inputText.setParent(this);
component = inputText;
} else if (attrObject.getAttributeDescriptor().getDataType() == DataTypeConstants.TEXT
&& enableInput == false) {
OutputLabel outputLabel = new OutputLabel();
outputLabel.setValue(attrObject.getAttributeValue());
outputLabel.encodeAll(context);
outputLabel.setId("nonEditatbleId");
component = outputLabel;
}
}
}
private ValueExpression createValueExpression(String valueExpression, Class<?> valueType) {
FacesContext facesContext = FacesContext.getCurrentInstance();
return facesContext.getApplication().getExpressionFactory()
.createValueExpression(facesContext.getELContext(), valueExpression, valueType);
}
Ok I think I found what caused all that mad performance problems. I did some logic inside a getter and because that getter was getting called multiple times that caused performance issues.

Primefaces p:orderList java backing list does not update

I am currently implementing a orderable list using PrimeFaces' component, embedded inside a . I was able to get the list to appear properly with my items. However, when I saved the list and submitted it back to the server, the rearranged items did not get reflected in the backing bean for some reason. Since the Primefaces showcase was able to see the changes, what am I doing wrong?
XHTML Snippet:
<h:form id="confirmDialogForm">
<p:confirmDialog id="arrangeProjDialog" widgetVar="arrangeDlg" width="600"
header="Meeting Order"
appendToBody="true" message="Drag and drop to rearrange meeting order">
<p:orderList id="arrangeProjDialogList"
value="#{adminMeetingListBean.orderProjList}"
converter="#{adminMeetingListBean.rowConverter}"
var="po"
controlsLocation="left"
styleClass="wideList"
itemLabel="#{po.projectTitle}"
itemValue="#{po}"
>
<f:facet name="caption">Proposals</f:facet>
</p:orderList>
<p:commandButton value="Save" ajax="true" process="arrangeProjDialogList #this"
actionListener="#{adminMeetingListBean.updateProposalMeetingOrder}" onclick="arrangeDlg.hide();">
</p:commandButton>
<p:button value="Cancel" onclick="arrangeDlg.hide(); return false;" />
</p:confirmDialog>
</h:form>
Backing Bean:
public void updateProposalMeetingOrder() {
if (selectedMeeting != null) {
orderProjTitles.get(0);
meetingService.updateMeetingProjSequence(orderProjList, selectedMeeting.getMeetingId());
}
}
The List is a list of POJO "ProposalOrderRow" objects. This has the definition:
public class ProposalOrderRow implements Serializable {
private static final long serialVersionUID = -5012155654584965160L;
private int dispSeq;
private int appId;
private int assignmentId;
private String refNo;
private String projectTitle;
public int getDispSeq() {
return dispSeq;
}
public void setDispSeq(int dispSeq) {
this.dispSeq = dispSeq;
}
public int getAppId() {
return appId;
}
public void setAppId(int appId) {
this.appId = appId;
}
public String getRefNo() {
return refNo;
}
public void setRefNo(String refNo) {
this.refNo = refNo;
}
public String getProjectTitle() {
return projectTitle;
}
public void setProjectTitle(String projectTitle) {
this.projectTitle = projectTitle;
}
public int getAssignmentId() {
return assignmentId;
}
public void setAssignmentId(int assignmentId) {
this.assignmentId = assignmentId;
}
}
Converter:
#FacesConverter("proposalOrderRowConverter")
public class ProposalOrderRowConverter implements Converter {
private List<ProposalOrderRow> orderRows;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String newValue) {
if (newValue.isEmpty()) {
return null;
}
for (ProposalOrderRow item : orderRows) {
String refNo = item.getRefNo();
if (refNo.equals(newValue)) {
return item;
}
}
return null;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
ProposalOrderRow row = (ProposalOrderRow) value;
String output = row.getRefNo();
return output;
}
public List<ProposalOrderRow> getOrderRows() {
return orderRows;
}
public void setOrderRows(List<ProposalOrderRow> orderRows) {
this.orderRows = orderRows;
}
}
This problem is caused by appendToBody="true" in the confirm dialog. Setting it to false solved the problem.
See link here: link

Can't keep faces message after navigation from preRender

in my preRender code for a page i add faces message then make navigation to another page as follows:
if(error){
addMessageToComponent(null,"AN ERROR HAS OCCURRED");
FacesContext.getCurrentInstance().getExternalContext().getFlash()
.setKeepMessages(true);
navigateActionListener("myoutcome");
}
and the util methods for adding message and navigation are:
public static String getClientId(String componentId)
{
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
UIComponent c = findComponent(root, componentId);
return c.getClientId(context);
}
public static UIComponent findComponent(UIComponent c, String id)
{
if (id.equals(c.getId())) { return c; }
Iterator<UIComponent> kids = c.getFacetsAndChildren();
while (kids.hasNext())
{
UIComponent found = findComponent(kids.next(), id);
if (found != null) { return found; }
}
return null;
}
/**
* #param componentId
* : the id for the jsf/primefaces component without formId:
* prefix. <br>
* if you use null then the message will be added to the
* h:messages component.
**/
public static void addMessageToComponent(String componentId, String message)
{
if (componentId != null)
componentId = GeneralUtils.getClientId(componentId);
FacesContext.getCurrentInstance().addMessage(componentId,
new FacesMessage(message));
}
public static void navigateActionListener(String outcome)
{
FacesContext context = FacesContext.getCurrentInstance();
NavigationHandler navigator = context.getApplication()
.getNavigationHandler();
navigator.handleNavigation(context, null, outcome);
}
but messages are not saved and so it doesn't appear after redirect.
please advise how to fix that.
The preRenderView event runs in the very beginning of the RENDER_RESPONSE phase. It's too late to instruct the Flash scope to keep the messages. You can do this at the latest during the INVOKE_APPLICATION phase.
Since there's no standard JSF component system event for this, you'd need to homebrew one:
#NamedEvent(shortName="postInvokeAction")
public class PostInvokeActionEvent extends ComponentSystemEvent {
public PostInvokeActionEvent(UIComponent component) {
super(component);
}
}
To publish this, you need a PhaseListener:
public class PostInvokeActionListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.INVOKE_APPLICATION;
}
#Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
#Override
public void afterPhase(PhaseEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().publishEvent(context, PostInvokeActionEvent.class, context.getViewRoot());
}
}
After registering it as follows in faces-config.xml:
<lifecycle>
<phase-listener>com.example.PostInvokeActionListener</phase-listener>
</lifecycle>
You'll be able to use the new event as follows:
<f:event type="postInvokeAction" listener="#{bean.init}" />
You only need to make sure that you've at least a <f:viewParam>, otherwise JSF won't enter the invoked phase at all.
The JSF utility library OmniFaces already supports this event and the preInvokeAction event out the box. See also the showcase page which also demonstrates setting a facesmessage for redirect.

Resources