This question already has answers here:
Display dynamic image from database or remote source with p:graphicImage and StreamedContent
(4 answers)
Backing beans (#ManagedBean) or CDI Beans (#Named)?
(5 answers)
Closed 2 years ago.
I'm relatively new to JSF and Primefaces.
I'm trying to build a document viewer that would display tiff, jpeg and pdf.
My UI has a left and a right layout.
Left layout contains a vertical grid of thumbnails of the docs.
User clicks on a thumbnail, and the right layout displays the doc (either via a p:graphicImage or via a p:documentViewer, depending on the thumbnail clicked).
For tiffs, I'm converting the image into PNG and then displaying (both, for the thumbnail and the main display).
For multi-page tiff files, I show only the first page as the left side thumbnail...and for the main display, I give Next and Previous buttons to navigate through the pages. The UI is something like this:
The problem:
When I click on the thumbnails, the backing method gets invoked twice as expected (the second call returning the StreamedContent with the actual data).
However, when I click the Prev and Next buttons, the same backing method is getting called only once -- just for phaseid=RENDER_RESPONSE.
(eventually, this causes the bean method to return only an empty StreamedContent and not one carrying the actual image data...so I don't get to see the other pages of the tiff)
The form:
<h:form id="mainform">
<p:layout fullPage="true" style="width:100%;height:100%">
<p:layoutUnit position="west" style="width:15%;overflow:scroll;" resizable="true">
<p:panelGrid header="Thumbnails" columns="1" class="thumbcell" >
<p:repeat value="#{viewerController.docIds}" var="docId">
<p:panelGrid style="padding:0px !important;" columns="1" rendered="#{docId.contains('img')}" >
<p:commandLink update=":mainform:renderpanel" action="#{viewerController.setSelectedDocId(docId)}">
<p:graphicImage value="#{viewerController.imageDocumentStream}" style="height: 100px; width: 90px; border: none !important; padding:0px;background: lightgrey;"
alt="#{viewerController.getDocumentName(docId)}">
<f:param name="id" value="#{docId}" />
</p:graphicImage>
</p:commandLink>
<p:outputLabel value="#{viewerController.getDocumentName(docId)}"/>
</p:panelGrid>
<p:panelGrid style="padding:0px !important;" columns="1" rendered="#{docId.contains('tif')}">
<p:commandLink update=":mainform:renderpanel" action="#{viewerController.setSelectedDocId(docId)}">
<p:graphicImage value="#{viewerController.tifDocumentStream}" style="height: 100px; width: 90px; border: none !important; padding:0px;background: lightgrey;"
alt="#{viewerController.getDocumentName(docId)}">
<f:param name="id" value="#{docId}" />
<f:param name="forThumbnail" value="true" />
</p:graphicImage>
</p:commandLink>
<p:outputLabel value="#{viewerController.getDocumentName(docId)}"/>
</p:panelGrid>
<p:panelGrid style="padding:0px !important;" columns="1" rendered="#{docId.contains('pdf')}">
<p:commandLink update=":mainform:renderpanel" action="#{viewerController.setSelectedDocId(docId)}">
<p:graphicImage value="#{viewerController.pdfFirstPageDocumentStream}" style="height: 100px; width: 90px; border: none !important; padding:0px;background: lightgrey;"
alt="#{viewerController.getDocumentName(docId)}">
<f:param name="id" value="#{docId}" />
</p:graphicImage>
</p:commandLink>
<p:outputLabel value="#{viewerController.getDocumentName(docId)}"/>
</p:panelGrid>
</p:repeat>
</p:panelGrid>
</p:layoutUnit>
<p:layoutUnit position="center" style="width:85%;">
<p:panel id="renderpanel">
<p:panelGrid style="padding:0px !important;" columns="1" rendered="#{viewerController.currentDocId.contains('tif')}">
<p:outputLabel value="#{viewerController.getDocumentName(viewerController.currentDocId)}"/>
<p:panelGrid style="padding:0px !important;" columns="3" id="tifPanelGrid">
<p:commandButton update=":mainform:renderpanel" action="#{viewerController.navigateTif('prev', viewerController.currentDocId)}"
id="prevButton" value="Prev" title="Previous page" disabled="#{viewerController.prevButtonDisable}"/>
<p:graphicImage rendered="#{viewerController.render}" id="tifPanel" value="#{viewerController.tifDocumentStream}" style="height: 600px; width: 800px; border: none !important; padding:0px;background: lightgrey;"
alt="#{viewerController.getDocumentName(viewerController.currentDocId)}">
<f:param name="id" value="#{viewerController.currentDocId}" />
<f:param name="forThumbnail" value="false" />
</p:graphicImage>
<p:commandButton update=":mainform:renderpanel" action="#{viewerController.navigateTif('next', viewerController.currentDocId)}"
id="nextButton" value="Next" title="Next page" disabled="#{viewerController.nextButtonDisable}"/>
</p:panelGrid>
</p:panelGrid>
<p:panelGrid style="padding:0px !important;" columns="1" rendered="#{viewerController.currentDocId.contains('img')}">
<p:outputLabel value="#{viewerController.getDocumentName(viewerController.currentDocId)}"/>
<p:graphicImage value="#{viewerController.imageDocumentStream}" style="height: 600px; width: 800px; border: none !important; padding:0px;background: lightgrey;"
alt="#{viewerController.getDocumentName(viewerController.currentDocId)}">
<f:param name="id" value="#{viewerController.currentDocId}" />
</p:graphicImage>
</p:panelGrid>
<p:panelGrid style="padding:0px !important;" columns="1" rendered="#{viewerController.currentDocId.contains('pdf')}">
<p:outputLabel value="#{viewerController.getDocumentName(viewerController.currentDocId)}"/>
<pe:documentViewer height="600" width="800" value="#{viewerController.pdfDocumentStream}" >
<f:param name="id" value="#{viewerController.currentDocId}" />
</pe:documentViewer>
</p:panelGrid>
</p:panel>
</p:layoutUnit>
</p:layout>
</h:form>
The controller class is annotated with these two:
javax.annotation.ManagedBean
javax.enterprise.context.SessionScoped
The backing method in the controller class, that is not getting invoked twice:
public StreamedContent getTifDocumentStream() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE ) {
return new DefaultStreamedContent();
} else {
String id = context.getExternalContext().getRequestParameterMap().get("id");
String forThumbnail = context.getExternalContext().getRequestParameterMap().get("forThumbnail");
Doc doc = tifDocMap.get(id);
InputStream is = this.convertTiffToPng(doc, forThumbnail);
return new DefaultStreamedContent(is);
}
}
I'm on this combination: SpringBoot + JoinFaces + PF 6.2
Can any body please advise what I'm doing wrong?
An alternate way I'm considering, to achieve the purpose:
May be, turn the pages of the tiff to individual PNGs beforehand, and then use galleria ...but it would be nice if I could get galleria to work with StreamedContent of each page dynamically.
Related
I have a panelGrid with two columns - each one of them with a panel. Both panels have dataTables and are side by side.
<h:panelGrid id="png31" columns="2">
<p:panel id="pnlCab" header="Cabecera">
<p:commandButton value="Nueva actividad" type="button" id="actiNue" onclick="PF('wdlgAgregar').show()"/>
<br /> <br />
<p:dataTable id="dataCabecera" value="#{cargaHorariaController.listaActivDoc}">
<!-- Elements -->
</p:dataTable>
</p:panel>
<p:panel id="pnlDet" header="Detalle">
<p:commandButton value="Nueva subactividad" type="button" id="subactiNue" onclick="PF('wdlgAgregarS').show()">
<p:ajax update="cmbSubNue" listener="#{cargaHorariaController.cargarSubactividades()}" />
</p:commandButton>
<br /> <br />
<p:dataTable id="dataDetalle" value="#{cargaHorariaController.listaSubactividad}">
<!-- Elements -->
</p:dataTable>
</p:panel>
When the datatables are empty, both panels are at the top, but when one of them displays records, the panel with the empty dataTable aligns vertically to the middle of the panel with the filled dataTable.
What can I do so both panels are always at the top, even with filled dataTables?
Try:
<style type="text/css">
.my-style td{
vertical-align: text-top !important;
}
</style>
<h:panelGrid id="png31" columns="2" columnClasses="width: 40%, width: 60%"
styleClass="my-style" >
<p:panel>
........
</p:panel>
.......
.......
.......
</h:panelGrid>
Add CSS classes:
.col1 {
width: 40%;
vertical-align: top;
}
.col2 {
width: 60%;
vertical-align: top;
}
and change
<h:panelGrid id="png31" columns="2" columnClasses="col1, col2">
I'm trying to offer some content dynamically, that is without dedicated pages, with the help of buttons. The aforementioned content is correctly loaded, but it's not working.
The layout is like this:
<p:outputPanel style="width: 100%; height: 100%; overflow: auto;" id="contentPanel">
<c:choose>
<c:when test="#{empty requestComposition}">
<ui:insert name="body">Content</ui:insert>
</c:when>
<c:otherwise>
<ui:include src="#{requestComposition}" />
</c:otherwise>
</c:choose>
</p:outputPanel>
The button to load content is like this (inside another outputPanel):
<p:commandButton value="Load Dynamic" update=":contentPanel" actionListener="#{applicationController.processActionButton('/dynamic')}" />
The action listener is like this:
public void processActionButton(String target){
if(target == null || target.isEmpty()){
return;
}
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest req = (HttpServletRequest)context.getExternalContext().getRequest();
req.setAttribute("requestComposition", "/WEB-INF/templates" + target + ".xhtml");
}
The dynamic.xhtml is like this:
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions"
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough"
xmlns:jsf="http://xmlns.jcp.org/jsf"
xmlns:p="http://primefaces.org/ui"
xmlns:pe="http://primefaces.org/ui/extensions">
<p:outputPanel style="width: 100%; padding-top: 20px;">
<p:outputPanel style="width: 60%; margin: 0 auto;">
<p:outputPanel style="text-align: center; color: gold; font-weight: bold;">Dynamic</p:outputPanel>
<p:messages id="messages" />
<h:form>
<p:panelGrid columns="2" style="width: 100%;">
<p:outputLabel value="Item" for="idItem" />
<p:outputPanel style="display: block; padding-right: 10px;" layout="inline">
<p:inputText value="#{bean.item}" id="idItem" style="width: 100%;" required="true" />
</p:outputPanel>
</p:panelGrid>
<p:outputPanel style="text-align: center;">
<p:commandButton value="Process" style="margin: 5px;" update=":growl" onclick="PrimeFaces.widgets.widget_#{fn:replace(component.clientId, ':', '_')}.disable()" oncomplete="PrimeFaces.widgets.widget_#{fn:replace(component.clientId, ':', '_')}.enable()" actionListener="#{applicationController.dummyAction}" />
<p:commandButton value="Dummy" style="margin: 5px;" update=":growl" actionListener="#{applicationController.dummyAction}" process="#this" />
</p:outputPanel>
</h:form>
</p:outputPanel>
</p:outputPanel>
</ui:composition>
When any of the buttons is clicked the form apparently submits, but the action listener is not executed.
When dynamic.xhtml is loaded as page content the form submits, but the action listener is triggered only for the second button (labelled Dummy), because of process #this, in which case the form is no longer submitted. I can workaround the form not submitting with process#this by adding onchange to input elements, but I failed to understand why is not working when it is loaded dynamically.
Thank you
I have to apologize to BalusC, since the answer he suggested was actually correct. I was tricked by the #PostConstruct which performed an unnecessary initialization for me, but the key is to use a #ViewScoped bean. That however raises another question, since it is stated in the documentation that by default there will be maximum 20 such objects stored in the server, which can be configured to a different value, but if I understood correctly that would mean that the 21st connection to the server will result in reusing the first one, so the new connection will access data not meant for it, or in discarding it and creating a new one, which means the first will lose its data?
I want to open a dialog box when selectbutton is pressed and that dialog box should show more detail related to the selected entry. my code is like this.
<p:commandButton id="selectButton" update=":form:display" oncomplete="moreviewDialog.show();" icon="ui-icon-search" title="View" style="width: 30px; height: 30px">
<f:setPropertyActionListener value="#{pMData}" target="#{managedBean.selectedRecord}" />
</p:commandButton>
<p:dialog header="History Data" widgetVar="moreviewDialog" resizable="false" id="moreviewDlg" showEffect="fade" hideEffect="explode" modal="true">
<h:panelGrid id="display" columns="2" cellpadding="4" style="margin:0 auto;">
<h:outputText value="CellId:" />
<h:outputText value="#{managedBean.selectedRecord.cellid}" style="font-weight:bold"/>
</h:panelGrid>
</p:dialog>
and managedBean is like this.
private PMData selectedRecord;
public void setSelectedRecord(PMData selectedRecord){
this.selectedRecord=selectedRecord;
}
public PMData getSelectedRecord(){
return selectedRecord;
}
But dialogbox does not show any value.
You forgot to provide an action in the commandButton
I have a simple JSF application with a form, one of the controls being a selectOneListbox:
<h:selectOneListbox style="width: 231px; height: 27px;position:absolute;left:400px;top:325px;" value="#{PatientsSearch.selectedDoctor}">
<f:selectItems value="#{PatientsSearch.doctors}" var="d" itemLabel="#{d.name}" itemValue="#{d.name}" />
</h:selectOneListbox>
When I run the application, I can see the list being populated (i.e. PatientsSearch.doctors is correctly retrieved), so I can choose one item from the selectOneListbox. After I choose the desired item, nothing works any more on that form. All the other buttons seem to be idle, they don't react to clicks any more. If however nothing is selected from the selectOneListbox, all buttons works as expected (i.e. they trigger their callbacks in the managed beans).
I debugged the application and noticed that indeed, after selecting an item in the list, the callbacks of the other buttons on the form don't get triggered when clicking them.
Also in the debugger I never see setSelectedDoctor() being called (see the snippet above) as I expected.
I'm using Mojarra 2.0.1 implementation of JSF.
What am I missing here ?
Update: This is the entire form:
<h:form style="width: 876px; background-color: #FED981; padding-left: 60px; padding-top: 30px; margin-left: 80px; margin-top: 40px; color: #804000; font-style: normal; font-family: Verdana, Arial, Sans-Serif">
<h:outputLabel style="position:absolute;left:200px;top:100px;" value="New appointment:"></h:outputLabel>
<br>
<br>
<h:inputText style="width: 259px;position:absolute;left:200px;top:120px;" binding="#{PatientsSearch.inputText1}" valueChangeListener="#{PatientsSearch.lookForName}" immediate="true" id="inputText1" />
<h:commandButton value ="Search patients by name:" style="width: 173px;position:absolute;left:465px;top:120px;" action="#{PatientsSearch.getPatientsByName}">
<f:param name="day" value="#{request.getParameter('day')}" />
<f:param name="month" value="#{request.getParameter('month')}" />
<f:param name="year" value="#{request.getParameter('year')}" />
</h:commandButton>
<br>
<h:panelGroup id="pn_DETAILS_GRP" style="overflow:auto;position:absolute;top:155px;left:200px;width:300px;height:150px;solid black">
<h:dataTable id="tb_USER_DETAILS" border="1" var="patient" value="#{PatientsSearch.patients}" style="width:300px;height:150px" rowClasses="oddRow, evenRow">
<h:column id="name">
<f:facet name="header">
<h:outputText value="Name" style="font-size:10pt"/>
</f:facet>
<h:outputText value="#{patient.name}" style="font-size:8pt"/>
</h:column>
<h:column id="phone">
<f:facet name="header">
<h:outputText value="Phone" style="font-size:10pt"/>
</f:facet>
<h:outputText value="#{patient.phoneMobile}" style="font-size:8pt"/>
</h:column>
<h:column>
<h:inputHidden id="patientId" value="#{patient.id}" />
</h:column>
</h:dataTable>
</h:panelGroup>
<h:inputHidden id="testId" />
<br><br><br>
<h:outputLabel value="Choose doctor:" style="width: 200px;position:absolute;left:200px;top:325px;"></h:outputLabel>
<h:selectOneListbox style="width: 231px; height: 27px;position:absolute;left:400px;top:325px;" value="#{PatientsSearch.selectedDoctor}" size="1">
<f:selectItems value="#{PatientsSearch.doctors}" var="d"
itemLabel="#{d.name}" itemValue="#{d.name}" />
</h:selectOneListbox>
<br><br><br>
<h:commandButton value="Schedule" style="position:absolute;left:200px;top:430px;" action="#{PatientsSearch.scheduleAppointment}">
<f:param name="day" value="#{request.getParameter('day')}" />
<f:param name="month" value="#{request.getParameter('month')}" />
<f:param name="year" value="#{request.getParameter('year')}" />
</h:commandButton>
</h:form>
As per comments, the problem is that the type for #{d.name} is not the same as #{PatientsSearch.selectedDoctor}. In such situation there is two solutions :
Keep everything you have the same, change the #{PatientsSearch.selectedDoctor} getter/setter to a String type.
Create a list of SelectItem in the backing bean and change the #{PatientsSearch.selectedDoctor} getter/setter to a String type. You will also be able to remove itemValue and itemLabel since the backing bean already provide ready Object for list components.
Change the itemValue attribute from the f:selectItems and create a custom converter
I prefer the second since it separate better the view. It should be changed like this :
<h:selectOneListbox style="width: 231px; height: 27px;position:absolute;left:400px;top:325px;" value="#{PatientsSearch.selectedDoctor}" size="1">
<f:selectItems value="#{PatientsSearch.doctors}" var="d" itemLabel="#{d.name}" />
<f:converter convertId="doctorConverter" />
</h:selectOneListbox>
Now for the converter, you can start it like this :
#FacesConverter(value="doctorConverter")
public class DoctorConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// Your code, select doctor from value ID for example.
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
// Your code, return doctor ID from value.
}
}
Im design a navigation bar for a webpage in jsf.
I want some links to be on the right of the bar and some on the left. It doesnt seem to work.
Any ideas?
<h:panelGrid columnus="2">
<h:panelGroup styleClass="alignmentLeft">
<h:panelGrid columns = "2" columnsClasses = "alignmentLeft">
<h:outputLink>... </h:outputLink>
<h:outputText/>
<h:outputLink>... </h:outputLink>
<h:outputText/>
</h:panelGrid>
</h:panelGroup>
<h:panelGroup styleClass="alignmentRight">
<h:panelGrid columns = "2" columnsClasses = "alignmentRight">
<h:outputLink>... </h:outputLink>
<h:outputText/>
<h:outputLink>... </h:outputLink>
<h:outputText/>
</h:panelGrid>
</h:panelGroup>
</h:panelGrid>
.alignmentRight {
text-align : right;
}
Try this out, there was a typing mistake in your coding as well, its the first h:panelGrid 's column you have typed "columnus"
Inside <h:head> the style sheet must be declared.
<h:head>
<h:outputStylesheet name="styles.css"
library="css" />
</h:head>
then in <h:body>
<h:body>
<h:panelGrid columns="2" width="600">
<h:panelGroup>
<h:panelGrid columns="2" columnClasses="alignmentLeft" width="200">
<h:outputLink>...1 </h:outputLink>
<h:outputLink>...2 </h:outputLink>
</h:panelGrid>
</h:panelGroup>
<h:panelGroup>
<h:panelGrid columns="2" columnClasses="alignmentRight" width="200">
<h:outputLink>...3 </h:outputLink>
<h:outputLink>...4 </h:outputLink>
</h:panelGrid>
</h:panelGroup>
</h:panelGrid>
</h:body>
In style sheet (styles.css) the styles are,
.alignmentLeft {
text-align : left;
border: 1px solid black;
background-color: orange;
}
.alignmentRight {
text-align : right;
border: 1px solid black;
background-color: lime;
}