I have a big problem with my Primefaces Interface. I want to do a loop over a list and display some information + a hidden editfield.
XHTML Primefaces codesnippet:
<p:dataList value="#{datas}" var="data">
<div class="ui-g">
<div class="ui-g-3">
<h2>#{data.desc}</h2>
</div>
<div class="ui-g-3">
<p:commandButton operation="edit" disabled="#{data.isLocked()}" actionListener="#{view.edit(data)}"
style="width:120px;" update="edit_#{data.id}" />
<p:commandButton operation="delete" actionListener="#{view.delete(data.getId())}" disabled="#{data.isLocked()}"/>
</div>
</div>
<!-- works perfectly to set the id -->
<span id="edit_#{data.id}">#{data.desc} #{index}</span>
<!-- doesnt work - maybe of the rendering moment to set the id? -->
<p:panelGrid id="edit_#{data.id}" rendered="#{view.edit}">
<p:outputLabel for="desc" value="#{msg.text}" />
<p:inputText id="desc" value="#{view.selectedValue.desc}" />
</p:panelGrid>
How can I set a dynamic ID to the panelGrid to update it by commandButton click if I want to edit that div? + How can I make the div toggled while editing it? or are there other Solutions? I am not allowed to use JavaScript/jQuery.
Thank you very much!
cheers,
JohnRamb0r
I would say you are nearly working against JSF and the way it works in your code example. Before showing you a working example, there are a few things I would like to say here related to good practice:
Do not call methods directly, use the built in translation of property references. A reference to data.locked will be translated to a call to data.isLocked() automatically. A call to data.locked is preferred, as it will cause the framework to evaluate it instead of you sending in the already evaluated value.
Work with JSF - not against it. There are a lot of unnecessary ids and use of unneeded tags and indexes in your example code. Keep it simple and work with the framework. Instead of referencing an id, referencing the object directly - it simplifies the code and makes it easier to use on the page itself.
Use action as the main executor of business logic and outcome. Action listeners are executed beforehand and can be used to intercept or stop the execution of the main action. They are are therefore suitable to be used as a validatory step before executing business logic.
Mark your events. It's good practice to use the naming convention on<Something> when naming methods that receive user events. This allows you to clearly identify them.
I made a small working example of your code (this uses Lombok and Apache Commons);
#Data
#Named
#ViewScoped
public class DataListViewBackingBean implements Serializable {
private Entity entity;
private Entity selectedEntity;
private List<Entity> dataEntities;
#PostConstruct
private void init() {
dataEntities = new ArrayList<>();
for (int i = 0; i < 10; i++) {
dataEntities.add(new Entity(i, RandomUtils.nextBoolean(),
RandomStringUtils.randomAlphabetic(30)));
}
}
#Data
#AllArgsConstructor
#EqualsAndHashCode(exclude = {"locked","description"})
public class Entity {
private int id;
private boolean locked;
private String description;
}
public void onEdit(Entity entity) {
selectedEntity = entity;
}
public void onDelete(Entity entity) {
dataEntities.remove(entity);
selectedEntity = null;
}
}
The code above initializes a data list of ten entities and fills this with random data. I took the privilege to change data to entity. When it comes to your HTML code, I felt it needed some cleaning. The definition of the JSF would look something like this;
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Data list test</title>
</h:head>
<h:body>
<h:form id="items">
<p:dataList type="definition" value="#{dataListViewBackingBean.dataEntities}" var="entity">
<div class="ui-g">
<div class="ui-g-8">
#{entity.description}
</div>
<div class="ui-g-4" style="text-align: right">
<p:commandButton value="Edit" disabled="#{entity.locked}" action="#{dataListViewBackingBean.onEdit(entity)}" update=":edit" />
<p:commandButton value="Delete" disabled="#{entity.locked}" action="#{dataListViewBackingBean.onDelete(entity)}" update="#form :edit" />
</div>
</div>
</p:dataList>
</h:form>
<h:form id="edit">
<p:outputPanel rendered="#{dataListViewBackingBean.selectedEntity != null}">
<h1>Editing...</h1>
<p:inputText placeholder="Description" value="#{dataListViewBackingBean.selectedEntity.description}" />
<p:commandButton value="Save" update=":items" />
</p:outputPanel>
</h:form>
</h:body>
</html>
Note how the editing div/outputPanel is wrapped in another container (a form). If we skipped the wrapping and instead pushed an update on the wrapped container directly, the rendered tag of the container would never be updated during a refresh and the div would therefore never show up. In this particular case, this is why the form is defined outside the container instead of inside.
This example uses a #ViewScoped bean, so as long as you stay on the page, the backing data should stay the same. If you reload the page you will get a new data set with new entities, as this will reinitialize a backing bean and call #PostConstruct again.
See also
How can I set id of a component/tag inside ui:repeat
Related
This is my problem. I have two <p: commandLink> inside a <ui: repeat>.
I need to update these buttons by simulating checkbox when I click them.
Updating a <panelGroup> around <ui:repeat> works. However for a list with many items, after clicking the button the update, it makes the screen go back to the top of the list making it impracticable to select many items. Are there any ways to give an update specific line in <p:commandLink>?
<tbody>
<ui:repeat value="#{myBean.itens}" var="item" varStatus="index">
<tr class="myCustomClass" id="item#{item.id}">
<th>
<p:commandLink
rendered="#{myConditionRendered}"
actionListener="#{myBean.deselect(item)}"
update=":panelGroupArroundId"
process="#this">
<i style="font-size: 15px;" class="fa fa-check-square-o" />
</p:commandLink>
<p:commandLink
rendered="#{!myConditionRendered}"
actionListener="#{myBean.select(item)}"
update=":panelGroupArroundId"
process="#this">
<i style="font-size: 15px;" class="fa fa-square-o" />
</p:commandLink>
</th>
<td>
<h:outputText value="#{item.field1}"/>
</td>
...
</tr>
</ui:repeat>
</tbody>
JSF version: 2.1
Primefaces version: 3.5
The trick here is to update only the sections you need - if you update the whole container the browser will reset and refresh from the point where the most recent update happened. In JSF there are a couple of ways to accomplish what you need. More recent versions of JSF (2.3 in particular) has selectors available such as :parent to allow to easier target specific parts of the page to update. With 2.1 you are slightly more limited.
I made a quick test and came up with a working example of your particular user case. The image below is using Font Awesome and PrimeFaces 3.5;
I slightly tweaked your code and made a complete and working example. The controller and backing bean have been separated into separate classes in this example. First let's take a look at the backing bean:
#Data #Named #ViewScoped
public class MyBean implements Serializable {
#Data #RequiredArgsConstructor
public static class Item {
#NonNull private String name;
private boolean selected;
}
private List<Item> items;
#PostConstruct
private void init() {
items = List.of(new Item("a"), new Item("b"), new Item("c"),
new Item("d"), new Item("e"), new Item("f"), new Item("g"),
new Item("h"), new Item("i"), new Item("j")
);
}
}
This defines our data set and uses Lombok to minimize the amount of boilerplate code (#Data and #RequiredArgsConstructor). I also added a selected flag to keep track of if the checkbox is selected or not. This is then used in the controller bean when selecting and deselecting:
#Data #Named #RequestScoped
public class MyBeanController {
#Inject
private MyBean myBean;
public void select(Item item) {
item.setSelected(true);
}
public void deselect(Item item) {
item.setSelected(false);
}
}
Furthermore, here is the view (XHTML) definition:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Button update in UI repeat example</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.0/css/font-awesome.css" />
</h:head>
<h:body>
<table>
<tbody>
<ui:repeat value="#{myBean.items}" var="item">
<tr style="float: left; padding-right: 10px">
<th>
<h:form>
<h:panelGroup id="content">
<p:commandLink rendered="#{item.selected}"
actionListener="#{myBeanController.deselect(item)}"
update="content" process="#this">
<i class="fa fa-check-square-o" />
</p:commandLink>
<p:commandLink rendered="#{!item.selected}"
actionListener="#{myBeanController.select(item)}"
update="content" process="#this">
<i class="fa fa-square-o" />
</p:commandLink>
</h:panelGroup>
</h:form>
</th>
<td><h:outputText value="#{item.name}"/></td>
</tr>
</ui:repeat>
</tbody>
</table>
</h:body>
</html>
As you can see we wrap the buttons inside a form and define an container content that we reference in the update attributes of the p:commandLink tags. Because we are wrapped inside a form the actual id of the panelGroup actually becomes something like j_idt6:9:j_idt8:content with each content id being unique (because each form has a unique id generated).
This allows us to target and update the data of that row specifically.
Theoretically you could update the form directly without wrapping it inside a content. However, doing so interferes with the life-cycle and handling of the form when wrapped inside a ui:repeat. Adding an additional panelGroup and updating that instead, like in this example, solves the issue.
I have a JSF application in wich i'm using ui:composition/ui:include to display some elements inside a page like in the following code
template.xhtml
...
<h:body>
...
<h:panelGroup id="mainPanel">
<ui:include src="#{myBean.page}"/>
</h:panelGroup>
...
</h:body>
items-list.xhtml
<ui:composition>
...
<h:form>
<p:dataTable var="item" value="#{myBean.items}">
<p:column>
<h:commandLink action="#{myBean.loadItemDetail(item)}"
process="#this"
update="mainPanel">
<h:outputText value="#{item.name}"/>
</h:commandLink>
</p:column>
</p:dataTable>
</h:form>
...
</ui:composition>
items-detail.xhtml
<ui:composition>
<h:form>
<!-- Some code to display and modify the item details-->
</h:form>
<ui:composition>
MyBean.java
#ManagedBean
#ViewScoped
public class MyBean {
private String page;
private List<Item> items;
private ItemDetail itmDetail;
#PostConstruct
private void init() {
page = "items-list.xhtml"
items = ... //some logic to populate the items list
}
public void loadItemDetail(Item item){
itmDetail = ... //some logic to get the item's detail
page = "item-detail.xhtml"
}
}
The first time I click on an item to see the details it works fine but after that, if I click on the browser's back button and try to load a different item details it keeps showing me the details of the first item.
I check the JSF lifeCycle for each call and although in every call the application goes through all the satages, only the first one calls the bean method locadItemDetail. is there a reason why my method is being ignored after the first success call? is there some kind of cache on JSF where my data is bean taken from instead of my Bean?
Also i tried to avoid this behavior implementing a filter as is suggest in this post and it kind of works but now when I click browser's back button it show me an annoying page preventing me from resend the form; is there a way to prevent that?
Note:
I know that i could change the app to use GET method instead of POST to avoid this cache problem but it will required some serious changes over the app so that's not an option.
Any help or guidance about why could this be happening will be appreciated.
I am facing a problem with commandButton, it is working only when the type is submit. Can someone take a look and let me know if there is a solution for that? The code below is very simple and does have the the propose to illustrate what I need. The method test() is not executed. Method runSubmit is executed successfully.
I need that test method is executed without a submit as the original page does have validations that are executed during the submit, test() method must be executed without a submit as it is a preliminary operation before of the submit.
I am using PrimeFaces 4.0, JDK 7, Tomcat 6 and JSF 2.0 (Apache), however I think it is happening in Mojarra as well.
SESSION:
package com.andre.bean;
public class AndreBean {
public void runSubmit() {
System.out.println("Submit executed");
}
public String test() {
System.out.println("Not submit executed");
return "true";
}
}
XHTML
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head>
</h:head>
<h:form id="test">
<p:commandButton id="ns" value="not submit" type="button" action="#{andreBean.test}" ajax="false"></p:commandButton>
<p:commandButton id="s" value="submit" action="#{andreBean.runSubmit}"></p:commandButton>
</h:form>
</html>
Thank you very much
Andre
What's going on?
What you get is correct behaviour. In PrimeFaces button with type="button" works as it does in basic HTML - it doesn't cause any request. As PrimeFaces user's guide says:
Push buttons are used to execute custom javascript without causing an
ajax/non-ajax request. To create a push button set type as "button".
<p:commandButton type="button" value="Alert" onclick="alert('Prime')" />
If you want to "talk to" bean, you need to use type="submit" (which is default in p:commandButton).
However... contrary to submit buttons behaviour in HTML, in PrimeFaces such submission will not force redirection to new page but all communication will be handled by underlying AJAX requests.
Therefore only your second button will execute beans' method:
<p:commandButton id="s" value="submit" action="#{andreBean.runSubmit}" />
What probably you wanted to obtain?
If you don't want to send all your form to bean you can limit the scope of components that are processed with "process" attribute of p:commandButton:
<h:form id="test">
<p:inputText value="#{andreBean.value}"/>
<p:commandButton id="s" value="submit" action="#{andreBean.runSubmit}" process="#this" />
</h:form>
With the following bean you will see the difference:
public class AndreBean {
private String value;
public void runSubmit() {
System.out.println("Submit executed");
}
public String getValue() {
System.out.println("getValue");
return value;
}
public void setValue(String value) {
System.out.println("setValue: " + value);
this.value = value;
}
}
If you don't limit executed components in console you get:
getValue
setValue: foobar
Submit executed
...and with components limited only to process="#this" you get only:
Submit executed
Hope that helps.
Sometimes, the solution is simply add immediate="true", it changes the point, in JSF lifecyle, in which the bean action is triggered.
Here are an article about how and when use it:
immediate attribute article
Please check your binding with bean.
bean fields should be String or non primitive.
I am learning JSF 2 at the moment. One of the first things I want to realize is the following scenario:
I have a commandLink on my page. When this link is clicked, an h:panelGroup-Tag is rendered (depending on the boolean attribute in the corresponding ManagedBean - which is false by default). I want that panelGroup to not be rendered when the link which "opened" the panelGroup is clicked again (that already works) but I want the panelGroup also not to be rendered when the user clicks outside of that panelGroup (that doesn't work).
The ManagedBean looks like:
#ManagedBean
#SessionScoped
public class LocaleBean {
#PostConstruct
public void init(){
locale = new Locale("de_DE");
showLanguageDiv = false;
}
private boolean showLanguageDiv;
public boolean getShowLanguageDiv() {
return showLanguageDiv;
}
public void setShowLanguageDiv(final boolean showLanguageDiv) {
this.showLanguageDiv = showLanguageDiv;
}
public void switchShowDivStatus(ActionEvent e) {
showLanguageDiv = !showLanguageDiv;
}
The facelet looks like:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core">
<h:form>
<div>
<h:commandLink style="float: right;padding: 2px" actionListener="#{localeBean.switchShowDivStatus}" value="#{msg.language}" >
</h:commandLink>
</div>
<h:panelGroup rendered="#{localeBean.showLanguageDiv}" style="clear:both;float:right;border:2px;border-style: solid" >
<p><h:commandButton .... /></p>
<p><h:commandButton .... /></p>
<p><h:commandButton .... /></p>
</h:panelGroup>
</h:form>
</ui:composition>
I already tried to add an hidden link which was clicked via javascript (when the user clicked anywhere in the body), which set the boolean attribute of the ManagedBean to false so that the panelGroup was not rendered. That worked partially because the panelGroup wasn't shown anymore. But on the other hand a click on the link to show the panelGroup had no effect anymore after doing that.
This is how it looked:
<h:body onclick="document.getElementById('invisibleform:invisiblelink').click()">
.
.
.
<h:form id="invisibleform">
<h:commandLink id="invisiblelink" actionListener="#{localeBean.switchShowDivStatus}"></h:commandLink>
</h:form>
</h:body>
So how can I resolve this issue? Is there a best practice of doing things like that? Was the hidden link the right approach? From what I've read so far it seems like this is something which should be done on client side via javascript. But all solutions I found were based on normal html-pages (non-JSF) and I can't image how this should be done when using JSF.
Thanks for any advice.
Are you able to use Primefaces along JSF? There is a Component <p:overlayPanel> See here for the showcase. Maybe that works for you...
I am creating a simple page that will, after clicking a button, replace one panelGroup with another. First, the code:
<h:body>
<ui:composition template="/elements/templateWithMenu.xhtml">
<ui:define name="content">
<div class="rightContent">
<h:panelGroup id="lol" rendered="#{test.firstStep.booleanValue()}">
<h3>This should disappear</h3>
<h:form id="newPollForm1" rendered="#{test.firstStep.booleanValue()}">
<fieldset>
<h:commandLink value="Next" action="#{test.firstStepCompleted()}" >
<f:ajax execute="#all" render="lol" />
</h:commandLink>
</fieldset>
</h:form>
</h:panelGroup>
<h:panelGroup rendered="#{test.secondStep.booleanValue()}">
Works!
</h:panelGroup>
</div>
</ui:define>
</ui:composition>
</h:body>
The backing bean simply sets the firstStep as false, and secondStep as true.
Now, when I tried running this, I got <f:ajax> contains an unknown id 'lol' - cannot locate it in the context of the component j_idt39. After googling a bit, I found out that for elements outside the form's scope, I need to use SEPARATOR_CHAR (:). That didn't work. So I tried messing with different combinations of #{component} and #{cc}, but nothing works. I even found this awesome explanation, but again, I failed miserably. If I use #all, everything goes ok (one panel is replaced with another), but I really need to render a specific component.
Help? Please?
You need to update the common parent <div class="rightContent"> instead. This one is always rendered and thus guarantees that JavaScript/Ajax can access and mainpulate its children. Replace it by <h:panelGroup layout="block"> and give it an id.
<h:panelGroup layout="block" id="content" class="rightContent">
<h:panelGroup rendered="#{test.firstStep}">
<h3>This should disappear</h3>
<h:form id="newPollForm1">
<fieldset>
<h:commandLink value="Next" action="#{test.firstStepCompleted()}" >
<f:ajax execute="#form" render=":content" />
</h:commandLink>
</fieldset>
</h:form>
</h:panelGroup>
<h:panelGroup rendered="#{test.secondStep}">
Works!
</h:panelGroup>
</h:panelGroup>
Using this Test.java class:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class Test {
private int number = 0;
public void firstStepCompleted() {
number++;
}
public boolean isFirstStep() {
return number == 0;
}
public boolean isSecondStep() {
return number == 1;
}
}
Note that I removed the superfluous Boolean#booleanValue() calls and the duplicated rendered contition on the form.
If that still doesn't work, then the /elements/templateWithMenu.xhtml apparently contains another naming container component which has prepended its ID. For starters who haven't memorized all naming container components yet, an easy way to figure the real right client ID is to open the page in browser, rightclick and View Source and then locate the JSF-generated HTML element and take exactly its id attribute value (and prefix it with : in the <f:ajax render>).