Text box Rendering in JSF 2.0 - ajax

I have a text box where the end user has to enter the number of text boxes that should be rendered. Based on the number entered, the number of text boxes have to render dynamically in row-format.
Also in the newly rendered row formatted text boxes the values entered has to be identified with correct backing bean when it goes to the next page.
Any suggestions on where to start or how to achieve something like this?

You can use <h:inputText> to ask enduser for input.
<h:inputText value="#{bean.number}" />
You can use <h:commandButton> to let user submit a form.
<h:commandButton value="Submit" action="#{bean.submit}" />
You can use List<T> to have a collection of items.
private List<Item> items;
public void submit() {
items = new ArrayList<Item>();
for (int i = 0; i < number; i++) {
items.add(new Item());
}
}
You can use <h:dataTable> to present it in rows.
<h:dataTable value="#{bean.items}" var="item">
<h:column>
<h:inputText value="#{item.value}" />
</h:column>
</h:dataTable>
The submitted values will just end up right there in items.

Related

How to postpone a keyup ajax request until h:inputText length is reached

Is there a way to postpone a keyup ajax request until a h:inputText value has reached a defined length?
I would like to reach the following goal:
a textInput field has to be filled with a combined date and time value. The expected format is: ddMMHHmm
Once the value reaches the length of 8 characters a new event object has to be added to an data list and should be displayed for confirmation immediately.
To confirm to add the new event the user simply presses enter inside this textInput field.
I don't know if there are different capabilities than using the ajax keyUp event to validate the input wihtout any further user interaction?
Here you see an very shortened example of my idea:
#Named
#SessionScoped
public class EventController {
private Date selectedDate; // +getter/+setter
private MyEvent event;
private List<MyEvent> events; // ArrayList<MyEvent>(), +getter
#PostConstruct
private void init() {
// load current events from DAO
}
public void validateInput() {
event = new MyEvent(selectedDate);
events.add(event);
}
public void confirmEvent() {
eventDAO.addEvent(event);
}
And the view:
<h:inputText
value="#{eventController.selectedDate}"
converter="#{comfortableDateTimeInputConverter}"
id="inputDateTime">
<f:ajax
<!-- pseudo code on !!! -->
executeCondition="<lengthOfInputField equals 8>"
<!-- pseudo code off !!! -->
execute="inputDateTime"
render="eventData"
event="keyup"
listener="#{eventController.validateInput}"
/>
</h:inputText>
<h:commandButton ... actionListener="#{eventController.confirmEvent}" />
<h:panelGroup id="eventData">
<h:dataTable var="..." value="#{eventController.events}">
// display event properties
</h:dataTable>
</h:panelGroup>
The ComfortableDateTimeInputConverter extracts the date an time parts of the input string and returns an date object.
I am using
primefaces 5.2
mojarra 2.2.8
Edit 1
As suggested by BalusC I modified my h:inputText, but nothing seems to happen. This is my original code exept the controller name. I've added a logging message inside eventController.validateNewEvent, but it seems not to be executed. Did I miss something?
<h:inputText
readonly="#{empty eventController.selectedPerson}"
value="#{eventController.selectedDate}"
id="inputDateTime"
tabindex="3"
converter="#{comfortableDateTimeInputConverter}"
onkeyup="return value.length >= 8"
onfocus="this.select()">
<f:ajax
event="keyup"
execute="inputDateTime"
listener="#{eventController.validateNewEvent}"
render="selectedDate txtDate listEvents" />
</h:inputText>
Also I tried to render="#all" at the ajax element, but still nothing happens. If i use event="blur" and leave the input with TAB it works like a charme ...
Edit 2 (resolved)
Replaced
onkeyup="return value.length >= 8"
with
onkeyup="return this.value.length >= 8"
and it works. See answer of BalusC ...
Just return false from onkeyup as long as value.length hasn't reached the desired value.
E.g.
<h:inputText ... onkeyup="return this.value.length >= 8">
<f:ajax event="keyup" ... />
</h:inputText>

How can I use ajax to submit h:selectOneRadio?

I'm having a problem getting things to work the way I think they should. I have a dataTable that I'm building from a list of benefits objects. Each of those objects can have one and only one type: group, account, or parent. I want to refresh the list when a radio button is selected. When the page loads, the group objects are displayed and the group radio button is pre-selected. If the user clicks the account radio button, I'm using Ajax to refresh the list, this time presenting Account objects. Here's the stripped down code:
XHTML:
<!-- Radio button to subset records -->
<h:selectOneRadio
id="varButtons"
value="#{benefitUI.buttonValue}"
styleClass="radio">
<f:selectItem
itemLabel="Group"
itemValue="G" />
<f:selectItem
itemLabel="Account"
itemValue="A" />
<f:selectItem
itemLabel="Parent"
itemValue="P" />
<p:ajax event="click" update="benList" process="#this" />
</h:selectOneRadio>
<p:dataTable
id="benList"
var="ben"
value="#{benefitUI.filteredBenefitList}"
rowIndexVar="vbRowIndex"
rowClasses="panel-grid-row-odd, panel-grid-row-even"
styleClass="mimic-datatable"
<p:column headerText="From" style="text-align:center;">
<h:outputText value="#{ben.beginDate}"/>
</p:column>
<p:column headerText="To" style="text-align:center;">
<h:outputText value="#{ben.endDate}"/>
</p:column>
</p:dataTable>
Java for BenefitUI:
#PostConstruct
public void init() {
/* Code that creates my unfiltered list removed for brevity. */
/** benefitList is the name of the unfiltered list **/
// Pre-select group on page load
setButtonValue("G");
}
public List<Benefit> getFilteredBenefitList() {
String filter = getButtonValue();
List<Benefit> filteredList = new ArrayList<Benefit>();
for (Benefit vb : benefitList) {
// The below code is working just fine, when filter is not blank
if ((filter.equals("G") & vb.getGroup() != null)
|| (filter.equals("A") & vb.getAccount() != null)
|| (filter.equals("P") & vb.getParent() != null)) {
filteredList.add(vb);
}
}
return filteredList;
}
public void setButtonValue(String bValue) {
this.buttonValue = bValue;
}
public String getButtonValue() {
return this.buttonValue();
}
The page loads just fine, displaying the group benefits. But when I click the Account button, an empty string is passed to the setButtonValue method, so when the filtered list is built, no records are included, and the page displays an empty list. What am I doing wrong that the itemValue isn't being sent to setButtonValue? Secondarily, why are the setButtonValue and the getFilteredBenefitList methods both being called four times?
I'm sure there's a keyword or something I'm missing, either on the Ajax line or in the selectItem, but I have no idea what it might be.

Creating Dropdown Box using AJAX with JSF/Primefaces

I am trying to create a simple dropdown box using Ajax and JSF 2.0/primeface. Based on First Dropdown selection second dropdown box is populated using AJAX call.
When I select first drop down it correctly populated the second dropdown box based on the Ajax call. But When I make selection in the second dropdown and click the button {which basically submit the form for some action}, It give error message
"formViewBusinessCode:selectedBusinessCode: Validation Error: Value is not valid"
When I check in console that is says the value for "selectedBusinessCode"{Id of second dropdown} is null. I am puzzled becuase it populates correctly but only after selection it gives error that value is not valid (basically null), Why the selected value is not reaching to the bean? Can someone please point what I am missing here, TIA
xhtml code is as below
<h:outputText styleClass="outputText" value="#{constant.businessCodeGroup}"></h:outputText>
<h:selectOneMenu id="selectedBusinessCodeGroup" value="#{viewBusinessCodeBean.selectedBusinessCodeGroup}" >
<f:selectItem itemValue="SELCT" itemLabel="Select Business Code Group" />
<f:selectItems value="#{viewBusinessCodeBean.businessCodeGroupList}" />
<p:ajax listener="#{viewBusinessCodeBean.getOnlyBusinessCodeListByAjaxCall}" event="change" update="selectedBusinessCode" process="#this"/>
</h:selectOneMenu>
<h:outputText styleClass="outputText" value="#{constant.businessCode}"></h:outputText>
<h:selectOneMenu id="selectedBusinessCode" value="#{viewBusinessCodeBean.selectedBusinessCode}">
<f:selectItem itemValue="SELCT" itemLabel="Select Business Code" />
<f:selectItems value="#{viewBusinessCodeBean.businessCodeList}" itemLable="#{viewBusinessCodeBean.businessCodeList.getlable}"
itemValue="#{viewBusinessCodeBean.businessCodeList.getValue}" />
</h:selectOneMenu>
<h:commandButton value="View" action="#{viewBusinessCodeBean.getOnlyBusinessCodeDescription}"></h:commandButton>
The bean coding is as below. it is a #ManagedBean
To Populate First Dropdown box
public ViewBusinessCodeBean() {
logger.entering(CLASS_NAME);
this.businessCodeGroupList = new ArrayList<SelectItem>();
List<String>tempBusinessCodeList = new BusinessCodeTableServices().getBusinessCodeGroupList();
Iterator<String>iterator = tempBusinessCodeList.iterator();
while(iterator.hasNext()){
String businessCodeGroup = iterator.next();
logger.debug(businessCodeGroup);
SelectItem item = new SelectItem(businessCodeGroup);
businessCodeGroupList.add(item);
}
logger.exiting(CLASS_NAME);
}
Ajax Call Method which populated second dropdown
public void getOnlyBusinessCodeListByAjaxCall() {
this.businessCodeList = new ArrayList<SelectItem>();
List<String>tempBusinessCodeList = new BusinessCodeTableServices().getOnlyBusinessCodeList(getSelectedBusinessCodeGroup());
Iterator<String>iterator = tempBusinessCodeList.iterator();
while(iterator.hasNext()){
String businessCode = iterator.next();
SelectItem item = new SelectItem(businessCode,businessCode,businessCode);
businessCodeList.add(item);
}
}
Your bean is apparently in the request scope. A request scoped bean is reconstructed on every request with all properties set to default. Your validation error is caused because businessCodeList property has become null/empty during the request of processing the form submit.
Putting the bean in the view scope should fix this problem.
See also:
Validation Error: Value is not valid
How to choose the right bean scope?

Ajax Listener event valueChange seems to be firing onClick instead of onChange

I have a nested list of Questions that I'd like to display. Initially, I'm displaying the Level 1 questions and then subquestions are displayed based on the users answers to the their parent question. All questions have a radio button and some questions have an input box for additional information that is shown when user selects "Yes"
Here is my JSF code with nested dataTables. Please note that I have pulled out formatting of these questions in order to simply the question on the forum, so these may look "unpretty" if you copy this code into your own environment and run the code:
<h:dataTable id="questionTable" var="q" value="#{generalQuestionBean2.questions.questions}">
<h:column><h:panelGroup id="questionGrp">
#{q.question} <h:selectOneRadio value="#{q.answer}">
<f:selectItem itemValue="1" itemLabel="Yes"/>
<f:selectItem itemValue="0" itemLabel="No"/>
<f:ajax event="valueChange" execute="#form"
render="questionGrp"
listener="#{generalQuestionBean2.reset}"/>
</h:selectOneRadio> <h:inputText value="#{q.addnInfo}"
rendered="#{q.answer eq '1' and q.field ne 'otherCov'}"></h:inputText>
<h:panelGroup id="questionGrpSubs" rendered="#{q.addnQuestions ne null and q.answer eq '1'}">
<h:dataTable id="subQuestionTable" var="subq" value="#{q.addnQuestions}">
<h:column><h:panelGroup id="subQuestionGrp">
->#{subq.question} <h:selectOneRadio id="answer" value="#{subq.answer}">
<f:selectItem itemValue="1" itemLabel="Yes"/>
<f:selectItem itemValue="0" itemLabel="No"/>
<f:ajax event="valueChange" execute="#form"
render="subQuestionGrp"
listener="#{generalQuestionBean2.reset}"/>
</h:selectOneRadio><h:inputText value="#{subq.addnInfo}"
rendered="#{subq.answer eq '1' and subq.field ne 'voluntaryComp' and subq.field ne 'uslh'}"></h:inputText>
<h:panelGroup id="questionGrpSubs2" rendered="#{subq.addnQuestions ne null and subq.answer eq '1'}">
<h:dataTable id="sub2QuestionTable" var="sub2q" value="#{subq.addnQuestions}">
<h:column><h:panelGroup id="sub2QuestionGrp">
-->#{sub2q.question} <h:selectOneRadio id="answer" value="#{sub2q.answer}">
<f:selectItem itemValue="1" itemLabel="Yes"/>
<f:selectItem itemValue="0" itemLabel="No"/>
<f:ajax event="valueChange" execute="#form"
render="sub2QuestionGrp"
listener="#{generalQuestionBean2.reset}"/>
</h:selectOneRadio><h:inputText value="#{sub2q.addnInfo}"
rendered="#{sub2q.answer eq '1'}"></h:inputText>
</h:panelGroup></h:column>
</h:dataTable></h:panelGroup>
</h:panelGroup></h:column>
</h:dataTable></h:panelGroup>
</h:panelGroup></h:column>
</h:dataTable>
Here is the code for the reset function on the backing bean:
private void reset(AjaxBehaviorEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
String id = event.getComponent().getClientId(context);
String[] tokens = id.split("[:]+");
int qId = -1;
int subqId = -1;
int sub2qId = -1;
for (int i = 0; i < tokens.length; i++) {
if(tokens[i].equals("questionTable"))
qId = Integer.parseInt(tokens[i+1]);
if(tokens[i].equals("subQuestionTable"))
subqId = Integer.parseInt(tokens[i+1]);
if(tokens[i].equals("sub2QuestionTable"))
sub2qId = Integer.parseInt(tokens[i+1]);
}
Question q = questions.getQuestion(qId);
Question processQ = q;
String defaultSubAnswer = getDefaultSubAnswer(q.getField());
Question subq;
Question subq2;
if(subqId > -1) {
subq = q.getAddnQuestions().get(subqId);
processQ = subq;
if(sub2qId > -1) {
subq2 = subq.getAddnQuestions().get(sub2qId);
processQ = subq2;
}
}
resetValue(processQ, defaultSubAnswer);
}
private void resetValue(Question q, String defaultSubAnswer) {
q.setAddnInfo("");
if(q.getAddnQuestions() != null) {
for (int i = 0; i < q.getAddnQuestions().size(); i++) {
Question subq = q.getAddnQuestions().get(i);
subq.setAnswer(defaultSubAnswer);
resetValue(subq, defaultSubAnswer);
}
}
}
Here is the problem:
The ajax event should default to "valueChange". If I click on "Yes" and then "Yes" again, the ajax call should not happen, correct? But it is, as the Additional Info box is clearing out based on the reset function.
I had originally tried adding a condition to the reset function to check the value of the button that was clicked and only reset the addnInfo value and subquestions if the answer is "0" (No). But this was causing issues with the rendering as the ajax call would render the input box and subquestions to hide and the value would be held onto on the front end, even though they're reset on the backing bean. When they re-rendered to the front in, the value that was held onto shows up instead of the value in the backing bean.
Another attempt was using a ValueChangeListener for the reset of the values. But this still has the same issue with the value being held onto when re-rendering.
I have tried 3 different approaches (listed above) and all have failed. I'm open to hearing a solution to any of these or possibly another solution. Keep in mind that formatting limitations by the users leaves me with less options to work with.
Ajax Listener event valueChange seems to be firing onClick instead of onChange
That's indeed the default valueChange event which is been used by the radio buttons and checkboxes which are genreated by the respective JSF components. Check the generated HTML source and you'll see that it's hooked to onclick. The reason why JSF does that by default is clear for checkboxes, but at first sight not entirely clear for radio buttons as they cannot be unticked anyway. The real reason is that it's done in order to ensure IE6/7 compatibility as onchange wouldn't be fired on 1st click in that browser.
If you don't care about IE6/7 users (whose distribution is however stongly decreasing lately), then change it to event="change" instead.
<f:ajax event="change" ... />
This way JSF will generate the event handler on onchange instead.
Update: you could use jQuery's .on('change') function binder which will fix the IE6/7 misbehaviour on the change event. Include the following in <h:head> if you're not already using jQuery:
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
and execute this function on load:
$(function() {
$(':radio').each(function() {
var handler = this.onclick;
this.onclick = null;
$(this).on('change', handler);
});
});
This will basically for every radio button move the JSF-generated onclick attribute to change event handler which is managed by jQuery so that it works consitent in all browsers, including IE6/7.
We can of course also do it with plain JS, but that would require lot of boilerplate and cross browser sensitive code.

h:inputText inside ui:repeater displays wrong value after an ajax update

I've got a JSF page with a ui:repeater tag that simply displays a list of strings and some controls to add a string to a list. When adding a string I use ajax to update the repeater tag and have the new string be shown immediately without the page refresh. Here's how my page looks like:
<h:body>
<h:form>
<p:inputText id="name" value="#{testController.newString}"/>
<p:commandButton value="Add" actionListener="#{testController.addString}" update="strings" />
</h:form>
<h:panelGroup id="strings">
<ui:repeat var="str" value="#{stringModel.strings}" varStatus="stringData">
<div>
<h:outputText value="#{str}" />
<h:inputText value="#{str}" />
</div>
</ui:repeat>
</h:panelGroup>
</h:body>
Everything works except the inputText component. After ui-repeater is updated with Ajax is still displays the text from the previous string. For example, assume that initially i have a list with 2 strings, "val1" and "val2". I enter a new string called "val3" and submit the form. List is updated correctly on the server side and the repeater is updated, it now has 3 elements. However, while the h:outputText in the newly added element will correctly show "val3", the inputText will be displayed with "val2" as a value. So i end up with something looking like this:
output tag input tag
val1 val1
val2 val2
val3 val2 (???)
The backing beans are very simple:
A view scoped model bean
#Component
#Scope("view")
public class StringModel {
private List<String> strings = Lists.newArrayList("Value 1");
public List<String> getStrings() {
return strings;
}
public void setStrings(List<String> strings) {
this.strings = strings;
}
}
And a request scoped controller bean:
#Component
#Scope("request")
public class TestController {
private String newString;
#Autowired private StringModel model;
public void addString() {
model.getStrings().add(newString);
}
public String getNewString() {
return newString;
}
public void setNewString(String newString) {
this.newString = newString;
}
}
I did some testing and this actually works the same way for any input component, be that textInput, textArea, etc. Any help would be highly appreciated.
I can't tell in detail exactly why it displays the wrong value after update (it'll be that the internal loop index of <ui:repeat> is broken — try a newer Mojarra version), but just referencing the string item by index from varStatus works. It'll also immediately fix the future problem of being unable to submit the edited string value when you put this list in a form, because the String class is immutable and doesn't have a setter.
<ui:repeat value="#{stringModel.strings}" var="str" varStatus="loop">
<div>
<h:outputText value="#{str}" />
<h:inputText value="#{stringModel.strings[loop.index]}" />
</div>
</ui:repeat>
EditableValueHolders inside ui:repeat are broken (by design) in the current version o JSF specs. It will not work, there is no way to fix it. Maybe new versions will make ui:repeat a proper component with support for saving states of its children. Maybe not.
If you change ui:repeat to h:dataTable, things should work (if not, then your problem is somewhere else and I was wrong).
Frankly, there is no workaround apart from using repeaters from some other libraries - you should find working repeaters in Tomahawk, Trinidad and many other places. Primefaces, AFAIR, does not have a pure repeater.
I also had exactly the same problem before. I solved it by putting the inputText in a form. I also copied your codes and put the h:inputText inside a h:form and it worked as well.
<h:form>
<ui:repeat value="#{stringModel.strings}" var="str" varStatus="loop">
<div>
<h:outputText value="#{str}" />
<h:inputText value="#{str}" />
</div>
</ui:repeat>
</h:form>

Resources