JSF 2.2 page, backing bean, page rendering? - jsf

I have a requirement where 2 input values are entered onto JSF 2.2 page. We are using Primefaces controls. These values are then submitted to managed bean method via h:command button.
Then, based upon the values, I want to set a separate output field on the same JSF page to a specific value.
So my issue at the moment is trying to wire a managed bean so that it returns a value back to field on my JSF page which in this case field name is mgrs. The main issue is that I'm dealing with is the third party library that produces the return values and I'm not sure the best approach on how to interface with this library so that I can return the values that I need from it. Also from within my JSF page what would be the best approach from the commandbutton to get the value back from the bean code?
Here is the working portion of my JSF page
<p:panel id="horizontal" header="Horizontal Toggle" toggleable="true"
toggleOrientation="horizontal">
<h:panelGrid columns="2" cellpadding="10" styleClass="left">
<h:outputLabel for="basic" value="Enter Latitude:" />
<p:inplace id="lat">
<p:inputText value="Latitude" />
</p:inplace>
<h:outputLabel for="basic" value="Enter Longitude:" />
<p:inplace id="long">
<p:inputText value="Longitude" />
</p:inplace>
<p:inplace id="mgrs">
<p:inputText value="Longitude" />
</p:inplace>
<h:commandButton actionlistener="#{coordinates.mgrsFromLatLon(lat, long)}" update="mgrs" />
Here is the third party API:
package com.berico.coords;
import gov.nasa.worldwind.geom.Angle;
import gov.nasa.worldwind.geom.coords.MGRSCoord;
import javax.faces.bean.ManagedBean;
#ManagedBean(name="coordinates")
public class Coordinates {
public static String mgrsFromLatLon(double lat, double lon){
Angle latitude = Angle.fromDegrees(lat);
Angle longitude = Angle.fromDegrees(lon);
return MGRSCoord
.fromLatLon(latitude, longitude)
.toString();
}
public static double[] latLonFromMgrs(String mgrs){
MGRSCoord coord = MGRSCoord.fromString(mgrs);
return new double[]{
coord.getLatitude().degrees,
coord.getLongitude().degrees
};
}
}

Yes Why not its quite simple.
Changes in JSF page
Put your elements inside a <h:panelGrid/> or something similar component.
Give one id to your <h:panelGrid id="panelID"/>
Bind your <h:outputtext/> with a variable from your bean.
Use rendered attribute in your <h:outputtext rendered="#{bean.showOutputBox}" />
Now in your button with action use render attribute and give id of pandelgrid which added in Step2
Changes in Java/bean side
Create a variable(boolean) showOutputBox = false with get/set method.
When you click on sumbitmethod call a action in bean ,in that action method make this(above) declare variable true rest JSF will take care.

Related

Auto calculated fields

I've the following form:
<h:form>
<h:inputText size="2" value="#{orderMB.quantity}" />
<h:outputLabel value=" #{orderMB.totalPriceOneItem} €" />
<h:commandButton value="submit" />
</h:form>
And I've the following method in a session scoped managed bean:
public void setQuantity(int quantity) {
this.quantity = quantity;
setTotalPriceOneItem(quantity* item.getItem().getPrice().doubleValue());
}
I would like to auto-update the total price result on every key press of the input field. How can I achieve this without pressing the submit button?
Your code isn't doing that anywhere. It's missing a <f:ajax>.
<h:inputText size="2" value="#{orderMB.quantity}">
<f:ajax event="keyup" render="total" />
</h:inputText>
<h:outputText id="total" value="#{orderMB.totalPriceOneItem} €" />
The event attribute can be set to any HTML DOM event on which JSF must submit the form by ajax, such as click, keyup, blur, focus, etc. The render attribute can be set to any JSF client ID which needs to be updated when the ajax submit finishes. In this case it's referring the ID of the component showing total price.
Note that I replaced the wrong <h:outputLabel> by <h:outputText>. Also noted should be that a setter isn't exactly the right place to perform business logic (a getter also not!). Better revert that setter method to a true setter and add an ajax listener method:
<f:ajax event="keyup" listener="#{orderMB.updateTotalPriceOneItem}" render="total" />
public void updateTotalPriceOneItem() {
totalPriceOneItem = quantity * item.getItem().getPrice().doubleValue();
}
In case when it still doesn't work, then verify if you have a <h:head> in the template instead of a <head>. If still in vain, work through commandButton/commandLink/ajax action/listener method not invoked or input value not updated.
That said, I strongly recommend to take a pause and work through a sane JSF 2.x book. The above is usually already covered in the 1st chapter.

<p:inputText> value not updated in model on change

I have a form that lets me edit a list of beans (one at a time), using buttons I can switch between the beans.
Keeping it simple :
public class MyBean {
private String text;
}
public class MyController {
private List<MyBean> availableBeans = new ArrayList<MyBean>(); // has five MyBeans with random text
private MyBean selectedBean; // initialized with first element of list
private int index = 0;
public void nextBean() { index++; }
public void previousBean() { index--; }
private void refreshBean() { selectedBean = availableBeans.get(index); }
}
For the html part I have something like
<h:form id="someForm">
<!-- stuff -->
<p:inputText value="#{myController.selectedBean.text}" />
<p:inplace editor="true" label="#{myController.selectedBean.text}" >
<p:inputText value="#{myController.selectedBean.text}" />
</p:inplace>
<!-- more stuff-->
</h:form>
If I change the text inside the inplace tag, the variable in myBean will be updated just fine, but If I only use inputText the bean will still have the old value, even if I change it on the webpage. Why is that?
Its because the p:inplace editor="true" implicitly submits the value to the server while <p:inputText does not do it implicitly,
You can solve it in several ways
1) add submit button like <p:commandButton to submit the value from p:inputText
2) use p:ajax event="keyup" or event="change",inside p:inputText
also take a look at the showcase p:ajax enables ajax features on supported components.
p.s , remove the value attribute from the p:inplace (there is no such attribute in p:inplace)
Lets give your components ids:
<h:form id="someForm">
<p:inputText id="first" value="#{myController.selectedBean.text}" />
<p:inplace id="second" editor="true" value="#{myController.selectedBean.text}">
<p:inputText id="third" value="#{myController.selectedBean.text}" />
</p:inplace>
</h:form>
According to the Primefaces Documentation 3.5 the component p:inplace has no attribute called value.
Do you submit the form someForm when changing the value of first? Otherwise the updated values from first won't be passed to MyController and MyBean. p:inplace submits the values automatically whereby you have to do it yourself it you use the standard p:inputText.

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! :)

Pass param via h:link using bean with the same view scope( has a value of null and thus will not be added to the URL)

I have one bean with view scope and want pass parametr between two different pages and on first page before this link I have <p:selectOneMenu/> where I choose test which id should be pass as GET param
<p:selectOneMenu value="#{addQuestion.test}" id="tests"
converter="#{testConverter}" required="true" requiredMessage="Please, choose test">
<f:selectItem itemLabel="--Select a test--" itemValue="" />
<f:selectItems value="#{addQuestion.testList}" var="testItem"
itemValue="#{testItem}" itemLabel="#{testItem.testName}" />
<p:ajax process="#this"
listener="#{addQuestion.getQuestionsBySubject()}"
update="addingQuestionsTable, testId" />
</p:selectOneMenu>
<h:link value="Add new question" outcome="addQuestion">
<f:param id="testId" name="testId" value="#{addQuestion.test.testIdentifer.testId}"/>
</h:link>
//in second page
<f:metadata>
<f:viewParam name="testId"
value="#{addQuestion.test.testIdentifer.testId}"
converter="#{testConverter}" required="true" requiredMessage="Invalid page access. Please use a link from within the system."/>
</f:metadata>
And bean
#ManagedBean(name = "addQuestion")
#ViewScoped
public class AddQuestion implements Serializable {
private Test test;
//get
//set
}
But when I try to get value on second page I nothing to get in expression #{addQuestion.test.testIdentifer.testId}" And also in development mode I get
has a value of null and thus will not be added to the URL.
My #{testConverter} is managed bean in view scope.
How I can pass testId in one bean with view scope and why I get this error?
The <f:xxx> tags like <f:param> doesn't generate any HTML and thus have nothing to update in the HTML DOM tree on ajax request. The <h:xxx> components are the ones which generate HTML and are updatable in the HTML DOM tree on ajax request. You need to update the <h:link> component instead of the <f:param> tag. So, move that id from <f:param> to <h:link>.
<h:link id="testId" value="Add new question" outcome="addQuestion">
<f:param name="testId" value="#{addQuestion.test.testIdentifer.testId}"/>
</h:link>

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