Using a4j:support with jsf 1.2 - jsf

I need to make some save action on h:selectonemenu. When it's value change then it should save this value, but without page refresh (so without submit).
Unfortunatelly I must work with jsf 1.2. After some research I've found that it can be done with a4j:support, however I have no idea how to include it into my project. Do I need to download some old richfaces libraries ? (as i know richfaces 4 doesnt support jsp syntax). Or does exists some other way to achieve this goal ??

1. You could use Richfaces 3.3.4.Final (downaload here).
This is way you should include, register and use libraries in a project. Notice:
A JSF application with RichFaces assumes that the following JARs are available in the project: commons-beanutils-1.7.0.jar, commons-collections-3.2.jar, commons-digester-1.8.jar, commons-logging-1.0.4.jar, jhighlight-1.0.jar.
Example (from developer guide) for your case:
<h:form id="planetsForm">
<h:outputLabel value="Select the planet:" for="planets" />
<h:selectOneMenu id="planets" value="#{planetsMoons.currentPlanet}" valueChangeListener="#{planetsMoons.planetChanged}">
<f:selectItems value="#{planetsMoons.planetsList}" />
<a4j:support event="onchange" reRender="moons" />
</h:selectOneMenu>
<h:dataTable id="moons" value="#{planetsMoons.moonsList}" var="item">
<h:column>
<h:outputText value="#{item}"/>
</h:column>
</h:dataTable>
2. Example of other solution:
You could use jQuery.ajax() with custom servlet.
JS:
$.ajax({
type: 'GET',
url: '/app/customservlet.jsf?value=' + selectOneValue; //selected value
});
Servlet:
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) {
String selectOneValue = req.getParameter("value");
//do something
}

Related

How update attribute of p:remoteCommand works

Primefaces 6.0. I understand that update attribute of p:remoteCommand should be used to specify clientIds of the components that should be updated by AJAX. I am trying to understand how PF works. In combination with DataTable it doesn't seem to work as expected. When I try to directly set update="form:dataTable:2:bColumn", it has no efect. However, doing this (commented out in the below code) RequestContext.getCurrentInstance().update("form:dataTable:2:bColumn"); will force PF to update the specified outputText.
Why is this happening? I will be happy for technical explanation - I am trying to find the answer by debugging PF Java/Javascript sources.
<h:form id="form">
<p:remoteCommand name="remoteCall"
action="#{grid4.onEdit}"
update="form:dataTable:2:bColumn"
/>
<p:dataTable id="dataTable"
var="gridItem"
value="#{grid4.gridItems}"
editable="true" editMode="cell"
>
<p:ajax event="cellEdit"
oncomplete="remoteCall()">
</p:ajax>
<p:column headerText="A">
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{gridItem.a}" /></f:facet>
<f:facet name="input"><p:inputText value="#{gridItem.a}"/></f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="B">
<h:outputText id="bColumn" value="#{gridItem.b}" />
</p:column>
</p:dataTable>
</h:form>
Bean
#ManagedBean
#ViewScoped
public class Grid4 {
private List<GridItem> gridItems = new ArrayList<>();
public Grid4() {
gridItems.add(new GridItem("1", "a","b"));
gridItems.add(new GridItem("2", "a","b"));
gridItems.add(new GridItem("3", "a","b"));
}
public void onEdit() {
System.out.println("onEdit()");
gridItems.get(2).setB("CHANGED VALUE");
// RequestContext.getCurrentInstance().update("form:dataTable:2:bColumn");
}
public List<GridItem> getGridItems() {
return gridItems;
}
public void setGridItems(List<GridItem> gridItems) {
this.gridItems = gridItems;
}
}
basically jsf ids an client side ids are two different things (check this answer and this post for a better understanding).
When you use RequestContext.getCurrentInstance().update("form:dataTable:2:bColumn"); that method use the client id to find the components that have to be updated, but in the case of the update property of p:remoteCommand it is expecting a jsf id, not the generated client id, so that´s why your update doesn't work. However, primefaces support jquery selectors to update components, so you could use a client side id on an update property like this update="#(#yourElementId)"
Let me start by mentioning that this is not specific to the p:remoteCommand. The reason for the behaviour you notice is rather simple although not directly obvious maybe since it is unfortunately not in the PrimeFaces documentation.
The update attribute in:
<p:remoteCommand name="remoteCall"
action="#{grid4.onEdit}"
update="form:dataTable:2:bColumn"
/>
uses a relative path if it does not start with a : and since the p:remoteCommand is already in the naming container with id='form', the form in the update attribute is superfluous and even makes it not work (run your app in dev mode, add a messages tag and see the errors).
So
<p:remoteCommand name="remoteCall"
action="#{grid4.onEdit}"
update="dataTable:2:bColumn"
/>
Should work, as should
<p:remoteCommand name="remoteCall"
action="#{grid4.onEdit}"
update=":form:dataTable:2:bColumn"
/>
The
RequestContext.getCurrentInstance().update("form:dataTable:2:bColumn");
is always absolute, so the colon is not needed here and it will find the element starting from the root (form 'prefix' is needed then)

f:ajax listener method argument

TODO: Get selected row in h:datatable.
Code Snippet using HtmlDataTable binding:
<h:dataTable value="#{bean.licenses}" var="license" rendered="#{!empty bean.licenses}" binding="#{bean.dataTable}">
<h:column>
<h:selectOneRadio onclick="uncheckOthers(this);" >
<f:selectItem itemValue="null" />
<f:ajax listener="#{bean.updateSelected}" render="licenseGenerator:submitButtons">
<f:param name="license" value="#{license}" />
</f:ajax>
</h:selectOneRadio>
</h:column>
</h:dataTable>
So the above mentioned is one way to do it, however since I am already using EL 2.2, I was trying to do something like the accepted answer. and update f:ajax to
<f:ajax listener="#{bean.updateSelected(license)}" render="licenseGenerator:submitButtons">
When I update the code with the above code, the listener is not getting invoked so I checked the documentation which says:
javax.el.MethodExpression (signature must match public void
processAjaxBehavior(javax.faces.event.AjaxBehaviorEvent event) throws
javax.faces.event.AbortProcessingException)
So, basically with the above signature I can't use something like
public void listener(License license){
//...
}
However, since it was the accepted answer, I am sure I am missing something here. Thanks for the help!
JSF Version: Mojarra JSF Implementation 2.2.12
Server: Apache Tomcat 8.0.24

JSF lazy loading component value

Consider a simple h:outputText component:
<h:outputText value="#{myBean.myValue}"/>
How can I lazy load that value after the page has been rendered, and display custom 'ajax loading' icon instead of the value while this is being done?
I am using PrimeFaces 3.5 in my project so any PF-specific implementation will be welcome.
A suggest to do this by calling remoteCommand after on page load (it is done by setting autoRun attribute to true) and update your outputText.
private String myValue;
// getter and setter
public void initMyValue() {
// init myValue
}
On page you should have ajaxStatus component for viewing loading image, and your outputText. Also there should be p:remoteCommand component:
<p:ajaxStatus style="width:16px;height:16px;" id="ajaxStatusPanel">
<f:facet name="start">
<h:graphicImage value="ajaxloading.gif" />
</f:facet>
<f:facet name="complete">
<h:outputText value="" />
</f:facet>
</p:ajaxStatus>
<h:outputText id="myText" value="#{myBean.myValue}"/>
<p:remoteCommand autoRun="true" actionListener="#{myBean.initMyValue}" update="myText"/>
EDIT: I supposed that you want to lazy load value of outputText because it contains some long running calculations, but if you want to completely deffer rendering of outputText first add boolean property in your backing bean, and set this property to true at the end of initMyValue method:
private boolean loaded;
// getter and setter
public void initMyValue() {
// init myValue
loaded = true;
}
on the page reorganize it as follows:
<h:panelGroup id="myPanel" layout="block">
<h:graphicImage value="ajaxloading.gif" rendered="#{!myBean.loaded}"/>
<h:outputText value="#{myBean.myValue}" rendered="#{myBean.loaded}"/>
</h:panelGroup>
<p:remoteCommand autoRun="true" actionListener="#{myBean.initMyValue}" update="myPanel"/>
You can use a BlockUI to conditionally block the component while it loads.
Define a preRenderComponent event on the <h:outputText/>
<h:outputText id="myText">
<f:event name="preRenderComponent" id="started"/>
</h:outputText>
Define a <p:blockUI/> with the id of the event as the trigger
<p:blockUI block="myText" trigger="started" />
You can customize the blockui to display an image or whatever.
A word of caution: I presume you require this because you're doing some heavy lifting in the getter of that component. Know that the getter will be called several times in the lifecycle of that page. So hiding the fact that the operation is taking a long time will not change the fact. A better design would be to preload and cache the value for that component in a durable scope, rather than the theatrics of a "loading" throbber.
This is how I ended up implementing it:
<h:panelGroup id="loginLocation">
<p:graphicImage library="assets" name="small-kit-loader.gif" width="16" height="16" rendered="#{empty mybean.lastLoginLocation}"></p:graphicImage>
<h:outputText value="#{myBean.lastLoginLocation}" rendered="#{!empty myBean.lastLoginLocation}"/>
</h:panelGroup>
<p:remoteCommand global="false" actionListener="#{actionBean.getUserLoginLocation(myBean.selectedUser)}" name="refreshLoginLocation" id="rc1" autoRun="true" update="loginLocation" process="#this"></p:remoteCommand>
Personally I am not entirely happy with this implementation:
lazy loading state is stored server-side, not client-side where it should be
I have to implement separate method on my backing bean (getUserLoginLocation) to retrieve the value, and explicitly store it in another property (lastLoginLocation). It would have been much cleaner just to have a single getter that is lazy-called after rendering the page in browser
Not easily reusable - depends on backing bean 'loaded' flag (#{empty myBean.lastLoginLocation} in this case), and requires action listener to actually set the value. Any composite component based on this approach would also depend on specific code in backing bean.
Any recommendations on how to improve this code are welcome! :)

JSF: invoking a javascript after a successful creation

my current code.
<h:form>
<h:panelGroup id="messagePanel" layout="block">
<h:messages errorStyle="color: red" infoStyle="color: green" layout="table"/>
</h:panelGroup>
<h:panelGrid columns="2">
// some form input stuff here..
</h:panelGrid>
<h:commandButton class="register-btn" action="#{accountController.create}" value="#{bundle.Register}">
<f:ajax event="action" execute="#form" render="messagePanel"/>
</h:commandButton>
</h:form>
messagePanel is where the validation errors displays.
create() method
public String create() {
try {
getFacadeUser().create(currentUser);
FacesContext.getCurrentInstance().getExternalContext().redirect("/index.xhtml");
return null;
} catch (Exception e) {
JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
return null;
}
}
is it possible to invoke a javascript after a successful creation? currently my create form is a pop up modal, what i want is to just hide the modal after a successful creation rather than redirecting to a page.
i'm using JSF 2.1
With standard JSF (read: no component libraries or utility libraries such as PrimeFaces or OmniFaces which have ways to make this easier), your best bet is to conditionally render <script> element.
<h:panelGroup id="script">
<h:outputScript rendered="#{not empty accountController.currentUser.id}">
alert('User successfully created!');
</h:outputScript>
</h:panelGroup>
Reference it in <f:ajax ... render="script">.
You can create a component binding for your dialog in your managed bean and hide the dialog from your function using that binding. I think the type of object which represents your component binding is framework specific (although all of them probably extend UIComponent), so you need to specify what JSF implementation you are using for a full solution. I implemented this very use case in ADF for example.
This is an example from ADF:
<af:popup id="sample" binding="#{viewScope.myBean.myPopup}">
and in the managed bean:
RichPopup myPopup;
...
public void onSave() {
//save user
myPopup.hide();
}

JSF dataTable with selectOneListbox

I have a dataTable that lists some objects and I want to set a property for those objects using a selectOneListbox. This is my dataTable
<h:dataTable value="#{someHandler.entities}"
binding="#{someHandler.dataTable}" var="entitiy">
<h:column>
<f:facet name="header">
<t:outputText value="Level" />
</f:facet>
<h:selectOneListbox id="level" value="#{entitiy.level}" size="1"
valueChangeListener="#{someHandler.changeLevel}"
onchange="submit()">
<f:selectItem itemValue="-" itemLabel="-" />
<f:selectItem itemValue="ALL" itemLabel="ALL" />
(and so on)
</h:selectOneListbox>
</h:column>
<h:column>
<f:facet name="header">
<t:outputText value="Name" />
</f:facet>
<h:outputText value="#{entitiy.name}" />
</h:column>
</h:dataTable>
The valueChangeListener looks like this:
public void changeLevel(ValueChangeEvent event) {
String newLevel = (String) event.getNewValue();
Logger logger = (Logger) dataTable.getRowData();
logger.setLevel(Level.toLevel(newLevel));
}
(dataTable is an HtmlDataTable object.)
However, the event object is always the same - no matter which row the selectOneListbox was in. (It seems always the logger in the first row). The Logger object I get is also not the one I want.
Any ideas? Thank you!
And anothers questions? Is the entitiy.setLevel() method called even though I have a valueChangeListener? I use entitiy.level because I want to show the chosen level as a default for those entity.
Thank you!
There are two potential problems here:
First, the onchange="submit()" submits the entire form. The valueChangeListener will be invoked on all input elements of which the submitted value differs from the initial value.
You need to preset the value behind #{entitiy.level} with the same value as the default menu option. E.g. in the constructor.
public Entity() {
level = "-";
}
Or, better, make the default value null.
<f:selectItem itemValue="#{null}" itemLabel="-" />
so that the valueChangeListener won't be invoked when the particular menu is not changed.
Or, when you are already on JSF 2.x (please always mention exact JSF impl/version in your JSF questions), you can use <f:ajax> tag for this without the need for a valueChangeListener with a hacky onchange="submit()".
Second, you need to ensure that the datatable value #{someHandler.entities} returns exactly the same list during the submit as it was during the initial request. So, do the data loading in the bean (post)constructor. In JSF 2.x you'd like to put the bean in the view scope as well.
Unrelated to the concrete problem, you can also just use <h:selectOneMenu> instead of a <h:selectOneListbox size="1">.

Resources