primefaces uiViewRoot find component inside <p:dataTable > - jsf

I am using primefaces 3.2.
I've got the following situation:
<h:form id="someForm">
..
<p:dataTable id="someDataTable" value="#{BackingBean.list}" var="element" ..>
<p:column>
<p:calendar id="someCalendar" value=#{element.date} ../>
<p:message id="someCalendarMessage" for=":someForm:someDataTable:someCalendar"/>
<p:column>
</p:dataTable>
..
</h:form>
The "someDataTable" is updated dynamically and then all the data are submitted to the server side. In the backing bean I want to send message for calendar "someCalendar" if the entered date doesn't satisfy some condition. I try to find the component using this code:
FacesContext facesContext = FacesContext.getCurrentInstance();
facesContext.addMessage(facesContext.getViewRoot().findComponent(":someForm:someDataTable:" + "i" + ":someCalendar").getClientId(), new FacesMessage(FacesMessage.SEVERITY_ERROR, Utils.getResourceBundleString("dictionary", "error") + ":", Utils.getResourceBundleString("dictionary", "some_message")));
return;
Where i is the index of the "element" in the dataTable which the same as index in the list located in the BackingBean.
The problem is that I get NullPointerException since the facesContext.getViewRoot().findComponent(..) method cannot find the component, although I checked the generated id of the calendar in the view and it is
:someForm:someDataTable:0:someCalendar for the first element and it should be :someForm:someDataTable:1:someCalendar for the next element etc.
Why the facesContext.getViewRoot().findComponent(..) method cannot find the component inside the ? The is updated dynamically and maybe the server side doesn't get the updated component tree after submission is done?
Thanks in advance.

This is because the p:dataTable is a repeated component and during view build time there is only one calendar in the view root. It has the id someForm:someDataTable:someCalendar.
Only during view render time the table rows are created and the row-dependent ids generated.
You should use JSF build-in validation facilities to check your dates:
in the view:
<p:calendar id="someCalendar" value=#{element.date}
validator="#{BackinBean.validateDate}"/>
<p:message id="someCalendarMessage" for="someCalendar"/>
(someCalendar in the for attribute is sufficient)
and in the backing bean:
public void validateDate(
FacesContext context,
UIComponent component,
Object value) throws ValidatorException {
if (/* date is not valid */) {
throw new ValidatorException(
new FacesMessage("Date is not valid"));
}
}
The FacesMessage from the validation method will automatically be displayed in the correct table row.

Related

I can't pass the param after clicking the h:commandLink

I'm trying to create a quizz with data from a db but I'm a bit lost.
I have 3 tables :
Quizz
Questions (ManyToOne: Quizz)
Answers (ManyToOne: Questions)
So I wrote this code to display each question with its answers:
<p>Questions</p>
<ui:repeat value="#{questionBean.fetchQuestionsFromQuizz(quizzBean.fetchQuizz(quizzBean.quizzId))}" var="i" varStatus="current">
<p>#{i.question}</p>
<h:selectOneRadio value="#{i.selectedAnswerId}">
<f:selectItems value="#{answerBean.fetchAnswersFromQuestion(i)}" var="item"
itemLabel="#{item.answer}" itemValue="#{item.id}" />
</h:selectOneRadio>
</ui:repeat>
I'm getting the "quizzBean.quizzId" through a GET param.
The value of h:selectOneRadio is the selectedAnswerId property in my Question entity, so I'm trying to bind it with the selected answer by the user.
So I'm trying to pass the quizz id in the submit button to check them in the bean(#RequestScoped), but the quizz id is null once clicking on the submit button...
Here's the button:
<h:commandLink value="Submit" action="#{questionBean.checkTheAnswers()}">
<f:setPropertyActionListener target="#{questionBean.fromQuizzId}" value="#{quizzBean.quizzId}" />
</h:commandLink>
And here's the method (a simple print):
public void checkTheAnswers(){
System.out.println("FROMQUIZZID : " + fromQuizzId);
}
So do you have any idea on why the quizzId is null?
You have a requestScoped managed bean and you pass the quizzId using the http GET method, in this case you shoud initialize the quizzId filed with the value from the GET method using the #PostConstruct init method of the managedbean.
#PostConstruct
public void init() {
try {
FacesContext facesContext = FacesContext.getCurrentInstance();
Map<String, String> params = facesContext.getExternalContext().getRequestParameterMap();
if (params.containsKey("quizzId") {
quizzId= params.get("quizzId");
....

p:autoComplete shows entity Id after submit

I'm using Primefaces version 5.3 autocomplete in a web project, I have written the search method and converter for the java entities i am searching for. These all work well the selected entity from the autocomplete is set correctly in the backing bean using a p:ajax tab and initially the entity name, which i specify in the itemValue, is set in the text input.
When i then submit the form that includes this autocomplete the variable in the backing bean which i set from the autocomplete stays as it is which is intended but the text input on the autocomplete will display the entities id instead of the name as i'd specified on the itemValue. This is because it is calling the toString method in my converter but I want it still to display the name yet i need the converter..
Has anyone come across this issue that may be able to help?
I have found a thread else where that describe this behaviour but it is a couple years old now and doesn't have an answer.
This thread is:
http://forum.primefaces.org/viewtopic.php?f=8&t=37918
As it may explain it better than i have...
Any help is appreciated.
UPDATE: code added
Here is my autocomplete tag, complete method returns a java List of entity type.
<h:form>
<p:autoComplete id="autocomplete" value="#{bean.selectedEntity}"
completeMethod="#{bean.listOfPossibleEntities}"
itemValue="#{_e}" itemLabel="#{_e.name}" autocomplete="off"
minQueryLength="3" var="_e"
placeholder="Enter Entity Name Here"
converter="EntityConverter" forceSelection="true">
<p:ajax event="itemSelect" update="enclosingForm"/>
<p:column>
<h:outputText value="#{_e.name}" />
</p:column>
</p:autoComplete>
<p:commandButton update="#form" >
</h:form>
FacesConverter looks like as below, i use a DAO call to our database find the object for each id
private EntityDAO entityDAO = (EntityDAO)Component.getInstance("entityDAO");
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
Integer id = Integer.valueOf(value);
return entityDAO.findById(id,false);
}
#Override
public String getAsString(FacesContext fc, UIComponent uic, Object object) {
String result = "";
if(object != null) {
if(object instanceof Entity){
result = ""+ String.valueOf(((Entity) object).getEntityId());
}
}
return result;
}
So yes on update of the h:form after the entity on the autocomplete is selected and the submitting with the p:commandButton the value display or the entity will change from the entity name to the entity id.
Hope this helps further thanks.

accessing PrimeFaces command button from bean to add action listener

I have the following command button in the view with ID "save":
<p:panel style="border:none;text-align:left;margin:0;">
<p:commandButton value="Save Document" id="save" icon="fa fa-save"
disabled="#{dIGRCController.digrc.qconce == '020'}">
<f:param name="validate" value="true" />
</p:commandButton>
<p:commandButton value="Clear" icon="fa fa-undo"></p:commandButton>
</p:panel>
I am trying to dynamically assign a different actionListener. If the user wants to INSERT some new record, I want it to call the insert method. If the user wants to update an existing record, it should call the update method.
Right now I am trying to do this:
#PostConstruct
public void init() {
// setting the action listener of the Save Document button
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
// UIComponent button = viewRoot.findComponent("save");
CommandButton button = (CommandButton) viewRoot.findComponent("save");
FacesContext context = FacesContext.getCurrentInstance();
MethodExpression methodExpression = context
.getApplication()
.getExpressionFactory()
.createMethodExpression(context.getELContext(),
"#{dIGRCController.updateDocument}", null,
new Class[] { DIGRCController.class });
button.addActionListener(new MethodExpressionActionListener(
methodExpression));
}
I am getting a null pointer exception on the line:
button.addActionListener(new MethodExpressionActionListener(
methodExpression));
What am I doing wrong? Is there another way to accomplish what I am trying to do? I am using JSF 2.2, PrimeFaces 5.3 and OmniFaces 1.11.
The findComponent() takes a client ID as argument not a component ID. The client ID is exactly the value of the generated HTML id attribute associated with the component in question. In case of a command button, usually the component ID of the parent <h:form> is prepended, separated by the naming container separator character which defaults to :.
Given this,
<h:form id="form">
<p:commandButton id="save" ... />
</h:form>
the client ID would be form:save.
CommandButton button = (CommandButton) viewRoot.findComponent("form:save");
See also this related question as to identifying and using client ID: How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
Unrelated to the concrete problem, manipulating the component tree in Java side is a poor practice. You'd better keep using XHTML+XML for this which is so much more self-documenting as to declaring/defining tree structures. You can use JSTL tags to dynamically build the view (note: this is different from dynamically rendering the view using rendered attribute!).
E.g.
<p:commandButton ... action="#{bean.save}">
<c:if test="#{bean.existing}">
<f:actionListener binding="#{bean.needsUpdate()}" />
</c:if>
</p:commandButton>
See also JSTL in JSF2 Facelets... makes sense?
Even more, you could just pass #{bean.existing} as method argument.
<p:commandButton ... action="#{bean.save(bean.existing)}" />
Both approaches are in turn admittedly kind of weird if #{bean.existing} refers the same bean as #{bean.save}. You could just check for that inside #{bean.save} itself.
public void save() {
if (existing) {
// UPDATE
} else {
// INSERT
}
}
Going further on that, this is IMO not the responsibility of frontend layer, but of the service layer. You pass the whole entity to the service layer which in turn checks based on PK if it's existing or not.
if (entity.getId() == null) {
// INSERT
} else {
// UPDATE
}

Primefaces selectOneMenu not displaying initial value

I set up a selectOneMenu with POJOs and a converter, in a p:dialog, see the sources below. It does work, except that initially, when it is first displayed in not-dropped-down state, the first choice is selected, not the one corresponding to the bean value. If I save the state of the selectOneMenu without interacting with it at all, the initially selected first choice is saved and so the real value is overwritten, but if I select a differenct choice, it is saved properly. The bean value to which the selectOneMenu is bound can't be null.
I debugged the converter, and it turned out, that when the backing data is loaded and the dialog is refreshed and displayed, all of the choices go through the converter's getAsString(), plus the choice for the real bean value again. Still, the first choice gets actually selected and displayed in the selectOneMenu. When the dialog's form is commited, the actually selected choice goes through the converter's getAsObject(), regardless whether that was the wrongly selected initial value or the manually selected one.
Please advise what might be the problem.
The xhtml of the button that invokes the dialog, this is in a different form:
<p:commandButton id="toolbarEditButton"
value="Edit selected" update=":editMediaForm"
disabled="#{!contentManager.mediaSelected}"
actionListener="#{contentManager.editSelectedMedia}"
onclick="PF('editMediaWidget').show()" />
The xhtml of the dialog:
<p:dialog id="editMediaDialog" widgetVar="editMediaWidget"
modal="true" resizable="false" >
<h:form id="editMediaForm" >
<p:panelGrid rendered="#{contentManager.isMediaSelected()}" columns="2" >
... <!-- other properties of the selected element -->
<p:outputLabel value="Media type" />
<p:selectOneMenu value="#{contentManager.selectedMedia.mediaType}"
converter="#{mediaTypeConverter}">
<f:selectItems value="#{mediaTypeConverter.allMediaTypes}"
var="mt" itemLabel="#{mt.name}" itemValue="#{mt}" />
<p:ajax listener="#{contentManager.onMediaTypeChanged()}" />
</p:selectOneMenu>
</p:panelGrid>
</h:form>
</p:dialog>
The converter:
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String stringId) {
Long id = Long.valueOf(stringId);
for (MediaType mt : mediaTypes) {
if (mt.getPkid().equals(id)) {
return mt;
}
}
return null;
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object mtObj) {
MediaType mt = (MediaType) mtObj;
return mt.getPkid().toString();
}
public List<MediaType> getAllMediaTypes() {
return mediaTypes;
}
Edit: the backing bean
#SessionScoped // javax.enterprise.context.SessionScoped
#Named("contentManager") // javax.inject.Named
public class ContentManager implements Serializable {
...
private List<Media> mediaList;
private Media selectedMedia = null;
...
public boolean isMediaSelected() {
if (selectedMedia == null) return false;
return true;
}
...
public void saveSelectedMedia() {
myDao.saveMedia(selectedMedia);
}
public void editSelectedMedia() {
// only for debugging, could be removed
}
}
The dialog is brought up and the form is updated by an edit button which is only available after an element is selected from a dataTable (selectedMedia). The update does seem to work, since the other properties of the selected element are properly updated and displayed in the dialog, so the bean value behind the selectOneMenu should be ok.
Update: of course I also examined the generated HTML. The <select> seems to be OK for me, it contains the proper values to be converted by the converter. (The selection is still wrong)
<select id="form:blah_input" name="form:blah_input" tabindex="-1">
<option value="1" selected="selected">1 - half_horizontal</option>
<option value="2">2 - half_vertical</option>
<!-- etc -->
</select>
The objects displayed in the SelectOneMenu have to have proper equals() method, not only the default Object#equals which is only true if they are the same object. This explains why the initial displayed value was always the first: the bean value never matched any of the possible values, so the SelectOneMenu component simply displayed the first one.
So the mistake was not in the JSF or backing bean code, but in the displayed domain objects' (MediaType) code. Adding the equals() method there solved the problem.

Filter Datatable from Backing Bean jsf-primefaces

I have a primefaces datatable and i have a column with filter.i would like to apply filter on the column from the backing bean.
I followed this example and i am able to get the input given filter text box into my bean.
but when i use setFilter ,the values are being set in the HashMap but filter is not being applied on the datatable.
Example column
<p:column filterBy="#{var.value}" headerText="Example" footerText="contains" filterMatchMode="contains" />
Bean is session scoped and the following code is in a function which gets called on a button click.
Map<String,String> theFilterValues = new HashMap<String,String>();
theFilterValues.put("filterColumn","someValue");
myDataTable.setFilters(theFilterValues);
this sets the values ,but there is no change on datatable.
i tried this but it did not help.
All i need is to set a filter on the datatable column upon a button click.
Thanks in advance
The values in the inputs of the DataTable filter are sent in the FacesContext request parameter map, and obtained by the DataTableRenderer when it is rendering the DataTable (see the encodeFilter method for PF 3.5, PF 4.0, or PF 6.1)
So, if your button is in the same form of the DataTable, the values of the filter are sent in the request parameter map, and the renderer will show those values over whatever else you want.
You'll need the button to be in a separate form:
<h:form>
<p:commandButton action="#{someBean.action()}" update="#([id$=dataTable])" />
</h:form>
<h:form>
<p:dataTable id="dataTable" [...] >
<p:column filterBy="#{var.col}" filterValue="#{someBean.filterValue}">
<h:outputText value="#{var.col}">
</p:column>
</p:dataTable>
</h:form>
And then, you can change the filterValue in the bean:
#Named
#SessionScoped
public SomeBean implements Serializable {
private String filterValue;
[...]
public void action() {
filterValue = "new value";
}
[getters/setters]
}
You can use a Map for the filterValues if you are using many filters.
As an alternative, if you need to redirect the user to a new page, you can put the values in the URL, instead of using filterValue. Example:
https://example.com/app/pageOfTheTable.xhtml?form:dataTable:colum:filter=new%20value
The part form:dataTable:colum:filter is the ID of the filter input. You can get that by inspecting the element using your browser. The principle is the same: you are using the request parameter map.
It may be useful to update the value of the backing bean when the user types something. I've found a patch here.
It basically changes populateFilterParameterMap method, in FilterFeature class, so it sets the value of the filterValue. You can put the added lines at the end of the for loop.
for ( ... ){
[...]
+ ValueExpression filterValueVE = column.getValueExpression("filterValue");
+ if (filterValueVE == null) {
+ ((UIComponent)column).getAttributes().put("filterValue", filterValue);
+ } else {
+ filterValueVE.setValue(context.getELContext(), filterValue);
+ }
}

Resources