I have implemented two StreamedContent beans to dynamic load a graphic image, following the solution found
here
1) With the first one I display images in a contentFlow (EVERYTHING OK)
<p:contentFlow value="#{imageBean.images}" var="image">
<p:graphicImage value="#{imageStreamer.fileContent}" styleClass="content" cache="false">
<f:param name="index" value="#{image.index}"/>
</p:graphicImage>
</p:contentFlow>
2) With the second one I try to display images using a foreach (or repeat):
<c:forEach items="#{imageBean.images}" var="item" varStatus="varStatus">
<p:graphicImage value="#{imageStreamer.fileContent}" cache="false">
<f:param name="index" value="#{varStatus.index}" />
</p:graphicImage>
</c:forEach>
This doesn't work. The check (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) is always true.
If I change the scope of the Bean to RequestScoped than it works! I'm confused... can someone help me?
Managed bean:
#ManagedBean
#ApplicationScoped
public class ImageStreamer{
public ImageStreamer(){}
public StreamedContent getFileContent(){
List<Link> links = this.getLinkItems();
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
FacesContext fc = FacesContext.getCurrentInstance();
String linkIndex = externalContext.getRequestParameterMap().get("index");
if(fc.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE){
return new DefaultStreamedContent();
}else{
int parsedIndex = Integer.parseInt(linkIndex);
Link ql = links.get(parsedIndex);
return new DefaultStreamedContent(new ByteArrayInputStream(ql.getBytes()), "image/png");
}
}
}
Since you are using primefaces, try using p:repeat
Repeat is an extension to standard repeat component to provide interoperability between JSF implementations and PrimeFaces components.
https://www.primefaces.org/showcase/ui/data/repeat.xhtml
Related
We have implement the datascroller from primefaces and extend it with lazy=true this works fine for the first lazy loading. But when I scroll down no event is fired from Frontend. The Backend works as expected.
I test it with changing the chunk size and get a better understanding what is happen in the backend code. We override the load function what primefaces is calling when you reach the end of page. I changed the code to not lazyloading by scrolling, therefor i implement a button to load the next chunk when you click the button. But Button disappear after clicking it once.
Xhtml:
<p:dataScroller value="#{stakeholderOverviewController.model}" var="stakeholder" chunkSize="50" lazy="true" rowIndexVar="test">
<f:facet name="header">
Scroll Down to Load More Cars
</f:facet>
<f:facet name="loader">
<p:commandButton type="button" value="More" icon="pi pi-chevron-circle-down"/>
</f:facet>
<h:panelGrid columns="2" style="width:100%" columnClasses="logo,detail">
<p:outputPanel>
<h:panelGrid columns="2" cellpadding="5">
<h:outputText value="Id:" />
<h:outputText value="#{stakeholder.lastname}" style="font-weight: bold"/>
<h:outputText value="Year:" />
<h:outputText value="#{stakeholder.firstname}" style="font-weight: bold"/>
</h:panelGrid>
</p:outputPanel>
</h:panelGrid>
<ui:include rendered="#{empty stakeholder}" src="/WEB-INF/compositions/stakeholderEmptyModel.xhtml" />
</p:dataScroller>
there is the backend code for Controller:
public StakeholderOverviewController() {
model = new LazyDataModel<StakeholderSearchWrapper>() {
#Override
public List<StakeholderSearchWrapper> load(int first, int pageSize, String sortField, SortOrder sortOrder,
Map filters) {
List<StakeholderSearchWrapper> execQuery = execQuery(first, pageSize);
return execQuery;
}
};
model.setRowCount(0);
}
....
protected synchronized List<StakeholderSearchWrapper> execQuery(int first, int pageSize) {
if (tmpfirst != first || tmppageSize != pageSize || reExecQuery) {
reExecQuery = false;
tmpfirst = first;
tmppageSize = pageSize;
tmpModel = new LinkedList<>();
// Query for stakeholder
queryresponse = stakeholderService.findByLastnameOrFirstmanOwnerOrShare(getSearchText(), getClientId(),
first, pageSize, selectedFacets, nameSort[getSort()]);
setFacets(queryresponse.getFacetFields());
Set<String> stakeholderIds = new HashSet<String>();
// convert for view
for (SolrDocument details : queryresponse.getResults()) {
StakeholderSearchWrapper stakeholderSearchWrapper = new StakeholderSearchWrapper(details);
tmpModel.add(stakeholderSearchWrapper);
stakeholderIds.add(stakeholderSearchWrapper.getId());
}
historyCount = stakeholderService.countHistoryEntryByStakeholders(stakeholderIds);
notesCount = stakeholderService.countNoteEntriesByStakeholders(stakeholderIds);
assignedProjects = projectService.findByStakeholderIds(stakeholderIds);
for(StakeholderSearchWrapper dao : tmpModel) {
dao.setHistoryCount(getHistoryCount(dao.getId()));
dao.setNoteCount(getNoteEntriesCount(dao.getId()));
dao.setProjects(getAssignedProjects(dao.getId()));
}
SolrDocumentList result = queryresponse.getResults();
int numFound = (int) result.getNumFound();
return tmpModel;
} else {
return tmpModel;
}
}
What i want is that the lazy loading dont fire only once. It should fire as often as needed. I dont know where i have to set some variable to do this. When im debuging the current code the load function is only requested at the first run of the page refresh and than one lazy loading is called but then nothing happens.
I think I realized the trick. Check the line:
model.setRowCount(0);
Instead, set the model row count properly using the max amount of data you want to display:
int totalNumberOfRegisters = this.count(/* relevant paramenters*/);
model.setRowCount(totalNumberOfRegisters);
I want to produce the result that whenever a tree node is expanded any previously expanded sibling node is collapsed.
Below are my page and my bean. The onNodeExpanded method is invoked when a node is expanded; the System.out statements produce the expected results. However, the node upon which setExpanded(false) is executed remains expanded on the page.
I added a commandButton so that I could force an update of the tree. Pressing it results in the page properly displaying the node that was previously collapsed by the ajax event listener.
The Page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>PrimeFaces Tree: Ajax Update Problem</title>
</h:head>
<h:body>
<h3>PrimeFaces Tree: Ajax Update Problem</h3>
<h:form>
<p:tree id="tree" value="#{problemController.root}" var="node" dynamic="true" orientation="horizontal">
<p:treeNode>
<h:outputText value="#{node}" />
</p:treeNode>
<p:ajax event="expand" listener="#{problemController.onNodeExpanded}" update="tree" />
</p:tree>
<h:commandButton value="Update" immediate="true">
<f:ajax render="tree" />
</h:commandButton>
</h:form>
</h:body>
</html>
The Bean:
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import org.primefaces.event.NodeExpandEvent;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;
#Named
#ViewScoped
public class ProblemController implements Serializable {
private static final long serialVersionUID = 1L;
private TreeNode root;
#PostConstruct
public void init() {
root = new DefaultTreeNode("Root", null);
TreeNode node0 = new DefaultTreeNode("Node 0", root);
TreeNode node1 = new DefaultTreeNode("Node 1", root);
TreeNode node00 = new DefaultTreeNode("Node 0.0", node0);
TreeNode node01 = new DefaultTreeNode("Node 0.1", node0);
TreeNode node10 = new DefaultTreeNode("Node 1.0", node1);
node1.getChildren().add(new DefaultTreeNode("Node 1.1"));
node00.getChildren().add(new DefaultTreeNode("Node 0.0.0"));
node00.getChildren().add(new DefaultTreeNode("Node 0.0.1"));
node01.getChildren().add(new DefaultTreeNode("Node 0.1.0"));
node10.getChildren().add(new DefaultTreeNode("Node 1.0.0"));
root.getChildren().add(new DefaultTreeNode("Node 2"));
}
public TreeNode getRoot() {
return root;
}
public void onNodeExpanded(NodeExpandEvent event) {
TreeNode expandedNode = event.getTreeNode();
System.out.printf("Expanded %s\n", expandedNode);
TreeNode parent = expandedNode.getParent();
if (parent != null) {
for (TreeNode sibling : parent.getChildren()) {
if (sibling.isExpanded() && sibling != expandedNode) {
sibling.setExpanded(false);
System.out.printf("Collapsed %s\n", sibling);
}
}
}
}
}
Java 1.8 PrimeFaces 6.1 Mojarra 2.3 Weld 3.0
Update 1
I have updated the page with the code that #Kukeltje provided. Unfortunately, the siblings of expanded nodes are not being collapsed. I added a statement to the closeOthers function that logs to the console: the data ("root", or "0", or "0_1", etc.) of any node that I expand is displayed (as expected).
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>PrimeFaces Tree</title>
</h:head>
<h:body>
<h3>PrimeFaces Tree</h3>
<h:form>
<p:tree id="tree" widgetVar="treeW" value="#{testController.root}" var="node" orientation="horizontal" selectionMode="single">
<p:treeNode>
<h:outputText value="#{node}" />
</p:treeNode>
<p:ajax event="expand" listener="#{testController.onNodeExpanded}" oncomplete="closeOthers(this, PF('treeW'))"/>
</p:tree>
<h:outputScript>
//<![CDATA[
function closeOthers(current, widget) {
var dataRowkey = $.urlParam(decodeURIComponent(current.data),current.source+"_expandNode");
var selector = "li[data-rowkey='"+dataRowkey+"']";
console.log("Collapsing siblings of " + selector);
$(widget.jq).find(selector).siblings().find("span[aria-expanded='true']").siblings(".ui-tree-toggler").trigger("click");
}
$.urlParam = function (query, name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(query);
return (results !== null) ? results[1] || 0 : false;
}
//]]>
</h:outputScript>
<h:commandButton value="Update" immediate="true">
<f:ajax render="tree" />
</h:commandButton>
</h:form>
</h:body>
</html>
It is not very common to update a full component from within ajax calls inside the component itself. Due to you using dynamic="true" you see less downside effects than without using dynamic="true". The same will happen if you update a container around the p:tree. There are effectively 2 updates on the tree that sort of conflict with eachother, especially when there is some additional hiding of nodes being attempted.
What you effectively seem to be trying to achieve is to have just one node open at a time by using server side manipulation of the tree (which in itself is not wrong). This is related indeed what is attempted in https://github.com/primefaces/primefaces/issues/2277 as well but that issue is too narrowly formulated since it is not about selecting/unselecting or like you experience hiding/unhiding, but the more general issue of not being able to update the full tree component from within an ajax call in the component.
Updating things server-side so a full refresh is still supported should still be done. But instead of doing an update="tree", you can try to use the oncomplete javascript callback to close all other nodes client-side.
Adding the following javascript will do this for you:
<h:outputScript>
//<![CDATA[
function closeOthers(current, widget) {
var dataRowkey = $.urlParam(decodeURIComponent(current.data),current.source+"_expandNode");
//For a vertical tree:
//var selector = "li[data-rowkey='"+dataRowkey+"']";
//$(widget.jq).find(selector).siblings().find("span[aria-expanded='true']").siblings(".ui-tree-toggler").trigger("click");
//For a horizontal tree
var selector = "td[data-rowkey='"+dataRowkey+"']";
// The first attempt was to look for aria-expanded=true but we
// found that sometimes it would be present on a collapsed node (bug?).
// $(widget.jq).find(selector).parent().parent().parent().siblings().find("td[aria-expanded='true']").find(".ui-tree-toggler").trigger("click");
// Searching instead for the minus icon did the trick
$(widget.jq).find(selector).parent().parent().parent().siblings().find(".ui-icon-minus").trigger("click");
}
$.urlParam = function (query, name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(query);
return (results !== null) ? results[1] || 0 : false;
}
//]]>
</h:outputScript>
Then
Give your widget a widgetVar e.g. widgetVar="treeW"
add an oncomplete in the p:ajax like so oncomplete="closeOthers(this, PF('treeW'))"
REMOVE dynamic="true" OR ADD cache="false" (bug I found, then it will only work once for each node, https://github.com/primefaces/primefaces/issues/3668)
REMOVE update="tree"
So the html ends up like:
<p:tree id="tree" widgetVar="treeW" value="#{problemController.root}" var="node" orientation="horizontal">
<p:treeNode>
<h:outputText value="#{node}" />
</p:treeNode>
<p:ajax event="expand" listener="#{problemController.onNodeExpanded}" oncomplete="closeOthers(this, PF('treeW'))"/>
</p:tree>
I am working with PrimeFaces messages, I want my whole page to scroll to top when p:messages is rendered.
Assign an ID to your p:message component
<p:messages autoUpdate="true" id="myMessage" />
Then, in your backing bean call RequestContext.scrollTo method:
in PrimeFaces >= 6.0:
PrimeFaces.current().scrollTo("myMessage")
in Primefaces < 6.0:
RequestContext context = RequestContext.getCurrentInstance();
context.scrollTo("myMessage");
which is deprecated in PrimeFaces 6.0
Deprecated with PrimeFaces < 6.2
In you backing bean (that one which produces the messages), you should know when you render a p:message. If so simply execute this:
RequestContext.getCurrentInstance().execute("window.scrollTo(0,0);");
Update:
With the newer PrimeFaces versions (>= 6.2), the approach to execute Javascript on the client side is (by using x and y coordinates):
PrimeFaces instance = PrimeFaces.current();
instance.execute("window.scrollTo(0,0);");
To scroll to an element use the element's clientId:
PrimeFaces instance = PrimeFaces.current();
instance.scrollTo("myElementsClientId");
Find more information here:
http://de.selfhtml.org/javascript/objekte/window.htm#scroll_to
examples with jQuery for smooth scrolling as well: Scroll to the top of the page using JavaScript/jQuery?
Lets say that your button is causing the messages to appear.
XHTML
<p:commandButton value="Save"
oncomplete="scrollToFirstMessage()" />
javascript
//javascript function which scroll to the first message in page
function scrollToFirstMessage() {
try {
PrimeFaces.scrollTo($('.ui-message :first-child').eq(0).parent().attr('id'));
} catch(err) {
//No Message was found!
}
}
Hope this helps.
There are valid answers already that show how to scroll to the p:messages component, but they all require you to execute code in a backing bean. This requires you to do / call the same in each action. None show how to scroll to the messages component when it is rendered (updated).
You can implement a phase listener and check messages are present and if the messages component's clientId is present in the PartialViewContext renderIds:
These client identifiers are used to identify components that will be processed during the render phase of the request processing lifecycle.
Your listener can look something like this:
public class MessagesUpdateListener implements PhaseListener {
private final String MESSAGES_ID = "yourMessagesClientId";
#Override
public void afterPhase(PhaseEvent event) {
// Empty
}
#Override
public void beforePhase(PhaseEvent event) {
FacesContext fc = FacesContext.getCurrentInstance();
if (!fc.getMessageList().isEmpty() &&
fc.getPartialViewContext().getRenderIds().contains(MESSAGES_ID)) {
RequestContext.getCurrentInstance().scrollTo(MESSAGES_ID);
}
}
#Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
}
Make sure to register it in your faces-config.xml:
<lifecycle>
<phase-listener>your.MessagesUpdateListener</phase-listener>
</lifecycle>
Tested with XHTML:
<h:form id="main">
<p:messages id="messages" />
<p:inputText id="text1" required="true" />
this<br/>is<br/>a<br/>long<br/>page<br/>this<br/>is<br/>a<br/>long<br/>page<br/>
this<br/>is<br/>a<br/>long<br/>page<br/>this<br/>is<br/>a<br/>long<br/>page<br/>
this<br/>is<br/>a<br/>long<br/>page<br/>this<br/>is<br/>a<br/>long<br/>page<br/>
this<br/>is<br/>a<br/>long<br/>page<br/>this<br/>is<br/>a<br/>long<br/>page<br/>
this<br/>is<br/>a<br/>long<br/>page<br/>this<br/>is<br/>a<br/>long<br/>page<br/>
this<br/>is<br/>a<br/>long<br/>page<br/>this<br/>is<br/>a<br/>long<br/>page<br/>
this<br/>is<br/>a<br/>long<br/>page<br/>this<br/>is<br/>a<br/>long<br/>page<br/>
<p:commandButton value="Update" update="messages text1"/>
<p:commandButton value="No update"/>
</h:form>
To check for global messages, use:
fc.getMessageList(null).isEmpty()
See also:
Add global message when field validation fails
I have been trying to upload an image with a p:uploadFile and it work fine at first time, but after the first uploaded file it not update the p:graphicImage, but if I reload the page the value of the graphicImage is fine and the image is showed. So, I think that is a problem with the primefaces uploadFile component, but I not sure.
My xhtml is this:
<h:form id="formNuevo" size="150%" enctype="multipart/form-data">
<h:outputLabel value="DiseƱo Neumatico: *" />
<p:fileUpload fileUploadListener="#{serviciosVentanaDiseno.manejarUploadedFile}" mode="advanced" auto="true" sizeLimit="9000000000" allowTypes="/(\.|\/)(gif|jpe?g|png)$/" update="mensajes graficImage"/>
<p:graphicImage id="graficImage" styleClass="disenoNeumatico" value="#{serviciosVentanaDiseno.streamedContentImagen}" ajax="true"/>
</h:form>
and my bean is a #SessionScoped bean, it's here:
public void manejarUploadedFile(FileUploadEvent event) {
UploadedFile uploadedFile = (UploadedFile)event.getFile();
try {
diseno.setImagen(uploadedFile.getContents());
streamedContentImagen = new DefaultStreamedContent(new ByteArrayInputStream(diseno.getImagen()));
} catch (IOException e) {
//log error
}
}
public StreamedContent getStreamedContentImagen() {
return streamedContentImagen;
}
public void setStreamedContentImagen(StreamedContent streamedContentImagen) {
this.streamedContentImagen = streamedContentImagen;
}
Try setting the cache attribute of the graphicImage component to false:
<p:graphicImage cache="false"
change scope #SessionScoped then <p:graphicImage cache="false">
I have issue when trying to make command link from inside p:galleria component
The problem is despite the fact at run time the link value value="Show present #{present.name} #{present.presentId}" contains the correct value of the id as example value="Show present Foo 1" , when pressing the command link it sends the wrong id of the second object every time
<h:form>
<p:galleria value="#{presentBean.allPresentList}" var="present" panelWidth="500" panelHeight="313" showCaption="true">
<f:facet name="content">
<h:commandLink value="Show present #{present.name} #{present.presentId}" action="pretty:present" actionListener="#{presentBean.setPresentObj}">
<f:attribute name="present" value="#{present.presentId}"/>
</h:commandLink>
</f:facet>
</p:galleria>
</h:form>
#ManagedBean(name="presentBean")
#SessionScoped
public class PresentBean implements Serializable{
ArrayList<Present> allUserPresentList = new ArrayList<Present>();
#PostConstruct
private void usersPresent(){
PresentDao presentDao = new PresentDaoImpl();
allPresentList = (ArrayList<Present>) presentDao.findAllPresents();
}
public ArrayList<Present> getAllUserPresentList() {
return allUserPresentList;
}
public void setAllUserPresentList(ArrayList<Present> allUserPresentList) {
this.allUserPresentList = allUserPresentList;
}
private String presentId ;
public String getPresentId() {
return presentId;
}
public void setPresentId(String presentId) {
this.presentId = presentId;
}
public void setPresentObj(ActionEvent ev){
Object presentOb = ev.getComponent().getAttributes().get("present");
if(presentOb != null){
this.presentId = (String) presentOb;
}else{
presentId = null ;
}
}
}
You need to use a setPropertyActionListener instead of <f:attribute name="present" value="#{present.presentId}"/> as the f:attribute tag is only evaluated when the component is created (only once) not when the component generates html based on the iterated rows.
So you'll need to instead use:
<f:setPropertyActionListener target="#{presentBean.presentId}" value="#{present.presentId}" />
That will set the value of the presentId in your managed bean, so in your action method you can just access the presentId itself already without having to work it out.
Alternatively if you're using a later version of JSF (using Servlet 3.0 or above), then you could create a method in the managed bean which takes the presentId or even the present object as a parameter
e.g. in your managed bean:
public void myAction(Present p){
//do whatever you want with the Present object
}
and in your .xhtml:
<h:commandLink value="Show present #{present.name} #{present.presentId}" actionListener="#{presentBean.myAction(present)}">
</h:commandLink>