PrimeFaces ManyCheckbox ArrayList not Updating inside modal dialog [duplicate] - jsf

This question already has answers here:
Understanding PrimeFaces process/update and JSF f:ajax execute/render attributes
(5 answers)
Closed 6 years ago.
I have a modal dialog, where a user can select and deselect roles based on another user and then submit this to the database to be updated.
However, after debugging, the ArrayList that backs the ManyCheckbox doesn't get updated, and the selectedRoles ArrayList remains to what it originally was.
For example:
I load the application
There is one user in the database with role 'admin'
I try to edit this user and the dialog opens up, 'admin' checkbox is selected.
I click the 'user' role checkbox and click submit
The selectedRoles array is still only just 'admin' instead of 'admin' and 'user'
Here is my dialog modal:
<p:dialog header="Editing User ID: #{usersView.viewUser}" id="editUserDialog" widgetVar="editUserDialog" modal="true" appendTo="#(body)">
<h:form id="editUserForm">
<p:selectManyCheckbox id="roleSelect" value="#{usersView.selectedRoles}" layout="grid" columns="3">
<f:selectItems value="#{rolesView.roles}" var="role" itemLabel="#{role.name}" itemValue="#{role.name}" />
</p:selectManyCheckbox>
<p:separator />
<p:commandButton process="#this" update=":form:tabs:adminView:userTable:userRoleOutput" value="Submit" id="EditUserSubmitButton" actionListener="#{usersView.editUserRole}" oncomplete="PF('editUserDialog').hide();" />
</h:form>
</p:dialog>
UserView:
#ManagedBean(name="usersView", eager=true)
#ApplicationScoped
private ArrayList<String> selectedRoles;
public Arraylist<String> getSelectedRoles()
{
return this.selectedRoles;
}
public void setSelectedRoles(ArrayList<String> roles)
{
this.selectedRoles = roles;
}
public void editUserRole(ActionEvent actionEvent)
{
// This method literally just loops through all users and matches the one we're looking at
User user = findUser();
if (user != null)
{
// gives user checked roles in database and local session
addSelectedRoles(user);
ArrayList<String> rolesToRemove = user.getRoleNames();
rolesToRemove.removeAll(selectedRoles);
// removes user unchecked roles in database and local session
removeSelectedRoles(user, rolesToRemove);
}
else
{
// Handle exception...
}
}
I am working on a restricted VM, so I cannot copy and paste and this is all the information I can post, which I believe is enough.
Any help is greatly appreciated.

I solved my issue by removing process="#this" and now the ArrayList is getting updated.
process="#this" means that the current component of the commandLink and hence why it wasn't updating.

Related

how to update dynamic text in PrimeFaces [duplicate]

This question already has answers here:
Can I update a JSF component from a JSF backing bean method?
(5 answers)
Closed 3 years ago.
I'm new to PrimeFaces (and JSF too) and I'm trying to update dialog message based on retrieved data.
View looks like:
<p:dialog id="userDialog" header="Confirmation" widgetVar="userDialog" dynamic="true" modal="true" closable="fasle" width="680px" resizable="false">
<h:form>
<b>Email address #{userListView.email}</b> is already registered with the following user <b>#{userListView.firstName}</b> <b>#{userListView.lasttName}</b>:
<b>Please confirm that you would like to proceed with adding an additional account for this User.</b><br />
<hr />
<div class="ui-confirm-dialog-footer">
<p:commandButton value="Yes" type="button" styleClass="ui-confirmdialog-yes" icon="pi pi-check" onclick="PF('userDialog').hide()" />
<p:commandButton value="No" type="button" styleClass="ui-confirmdialog-no" icon="pi pi-times" onclick="PF('userDialog').hide()" />
</div>
</h:form>
And controller looks like:
#ManagedBean(name = "userListView")
#ViewScoped
public class UserListView extends DataTableListView implements Serializable {
.....some other code...
public String getFirstName() {
String firstName = "";
List<UserMembership> membershipList = getMembershipList();
if (!membershipList.isEmpty()) {
UserMembership membership = membershipList.get(0);
firstName = membership.getUser().getFirstName();
}
return firstName;
}
public String getLasttName() {
String lastName = "";
List<UserMembership> membershipList = getMembershipList();
if (!membershipList.isEmpty()) {
UserMembership membership = membershipList.get(0);
lastName = membership.getUser().getLastName();
}
return lastName;
}
public String getEmail() {
String email = "";
List<UserMembership> membershipList = getMembershipList();
if (!membershipList.isEmpty()) {
UserMembership membership = membershipList.get(0);
email = membership.getUser().getEmail();
}
return email;
}
....some other code...
}
and dialog box is spawned as
RequestContext.getCurrentInstance().execute("showDialog(userDialog')");
It will work fine for the first user but then when I will get that dialog box for another one it keeps data from first one not updated with current.
Also I have try to add
onHide="PF('userDialog').content.empty()"
to the dialog but that will remove whole dialog box content.
How can I destruct and refresh that dialog box with new data then?
PS.
Not sure if I need to get this done via
#PostConstruct
public void init() {}
In the case you are using Primefaces 7.0+ swap the already mentioned
RequestContext.getCurrentInstance().update("userDialog");
for
PrimeFaces.current().ajax().update("userDialog");
If I can suggest you, set some id for <h:form> and use the full client ID after, something like
PrimeFaces.current().ajax().update("userDialog:yourFormId");
BalusC already answer a similar question in this thread Can I update a JSF component from a JSF backing bean method?
In my case fix looks like
RequestContext.getCurrentInstance().update(":userDialog");
just after .execute() part.

Refreshing p:inputText content after clicking button

I'm implementing simple registration form. Table with inputs and labels is placed in p:dialog which is placed in h:form. One of the feature is automatically generated alphanumeric password. I want to update value of password p:inputText after clicking button which is placed in the same form. Generating method is called every time I push the button (and I can see generated strings in console log). The problem is related with the inputText tag which is not updated properly. After opening the dialog box I can refresh password inputText only once, next time method is fired but inputText is not updated. The code is as below:
Alphanumeric password generator method:
public static String randomPassword(){
return RandomStringUtils.randomAlphanumeric(PASSWORD_LENGTH);
}
New account model:
#Named
#ViewScoped
public class AccountModel implements Serializable {
private Account Account = new Account();
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
Password fixing method in ViewScoped AddUserBean:
public void generateRandomPassword() {
accountModel.getAccount().setPassword(PasswordGenerator.randomPassword());
LOGGER.info(MessageFormat.format("Random password generated: {0}", accountModel.getAccount().getPassword()));
}
JSF form snippet:
<p:column>
<p:inputText id="userPassword" value="#{accountModel.account.password}" styleClass="fullWidth" />
</p:column>
<p:column>
<p:commandButton id="randomPasswordButton" update="userPassword">
<p:ajax event="click" listener="#{addUserBean.generateRandomPassword}" update="userPassword" />
</p:commandButton>
</p:column>
The question is: What kind of updating should I use for proper password inputText refreshing (every time I push the button, not only once)?

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.

primefaces uiViewRoot find component inside <p:dataTable >

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.

Resources