This question already has answers here:
How to ajax-refresh dynamic include content by navigation menu? (JSF SPA)
(3 answers)
Closed 6 years ago.
I'm working on a schoolproject and encountered a problem with dynamic ui insert.
I'm currently working in JSF2.0 / Primefaces 3 M1.
The main problem is that I want to have a Primefaces dialog with dynamic content which is changed through means of a menubar.
The dialog component contains a ui:include tag with src set to a managed bean property.
The menuitems are linked to bean methods that change this property.
The main problem is that the variable is not changing at all, I'm currently using alerts to show this variable.
Currently working with remoteCommand but I've also already tried with action/actionlistener in the menuitem component. Any help would be appreciated.
This is the main page with the menubar and the dialog.
<?xml version='1.0' encoding='UTF-8' ?>
<!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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>KunstInHuis: Main</title>
</h:head>
<h:body style="font-size: 12px;">
<h:form id="mainForm">
<f:metadata>
<f:event type="preRenderView" listener="#{login.verifyAccess}"/>
</f:metadata>
<p:remoteCommand name="lazyANew" actionListener="#{main.goToAboNew}" onsuccess="alert('test')"/>
<p:remoteCommand name="lazyAList" actionListener="#{main.goToAboList}" onsuccess="alert('test')" />
<p:remoteCommand name="lazyASearch" actionListener="#{main.goToAboSearch}" onsuccess="alert('test')" />
<p:menubar autoSubmenuDisplay="true">
<p:submenu label="Abonnementen">
<p:menuitem value="Nieuw" onclick="lazyANew()" onsuccess="alert('#{main.goToPage}')"/>
<p:menuitem value="Lijst" onclick="lazyAList()" onsuccess="alert('#{main.goToPage}')" />
<p:menuitem value="Zoek" onclick="lazyASearch()" onsuccess="alert('#{main.goToPage}')"/>
</p:submenu>
<p:submenu label="Klanten">
<p:menuitem value="Nieuw" url="#" />
<p:menuitem value="Lijst" url="#"/>
</p:submenu>
<p:submenu label="Kunstwerken">
<p:menuitem value="Nieuw" url="#" />
<p:menuitem value="Lijst" url="#"/>
</p:submenu>
</p:menubar>
</h:form>
<p:dialog id="mainDialog" header="Abonnement aanmaken" widgetVar="dlg0" minHeight="300" minWidth="450"
resizable="true" modal="false" >
<ui:include src="#{main.goToPage}" />
</p:dialog>
</h:body>
And this is my backing bean.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.page.beans;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.ActionEvent;
/**
*
* #author wezlee
*/
#ManagedBean(name = "main")
#ViewScoped
public class mainMenuBean implements Serializable {
private boolean panelRender=false;
private String goToPage = "./includes/forms/abboCreate.xhtml";
/** Creates a new instance of mainMenuBean */
public mainMenuBean() {
}
public void goToAboNew(ActionEvent e){
goToPage="./includes/forms/abboCreate.xhtml";
}
public void goToAboNew(){
goToPage="./includes/forms/abboCreate.xhtml";
}
public void goToAboList(ActionEvent e){
goToPage="./includes/forms/abboList.xhtml";
}
public void goToAboList(){
goToPage="./includes/forms/abboList.xhtml";
}
public void goToAboSearch(ActionEvent e){
goToPage="./includes/forms/abboSearch.xhtml";
}
public void goToAboSearch(){
goToPage="./includes/forms/abboSearch.xhtml";
}
/**
* #return the goToPage
*/
public String getGoToPage() {
return goToPage;
}
public void setGoToPage(String src){
this.goToPage=src;
}
}
Actually, I think the methods to update the backing value look like they work just fine. The issue is probably in how you are trying to display it. When the page is initially rendered, the value for goToPage is evaluated and placed into the content of the page returned to the user's browser. Once this value is set on the user's side, it will not re-synchronize with the backing bean until that part of the page is re-rendered.
I believe a prime faces remoteCommand allows you to do this with AJAX using update="client side id". I'm not a prime faces guy though, so I put together this little test. First change your remoteCommands:
<p:remoteCommand name="lazyANew" actionListener="#{main.goToAboNew}" update="testOutput" />
<p:remoteCommand name="lazyAList" actionListener="#{main.goToAboList}" update="testOutput" />
<p:remoteCommand name="lazyASearch" actionListener="#{main.goToAboSearch}" update="testOutput" />
Then add a simple text field to your page content with the appropriate Id:
Page target: <h:outputText value="#{main.goToPage}" id="testOutput" />
Your output text should start synchronizing. As long as that works, you can simply re-target the update from testOutput to mainDialog and you should be in business.
Related
I am having trouble to update the view from the bean in the back using PrimeFaces's RequestContext. In the example below I have a button and 2 panels. When pressing the button, I want to update one panel, but not the other one.
It does not work though and I can't find the error! requestContext.update("panela"); is fired, but doesn't do its job!
Help greatly appreciated!
The XHTML file:
<!DOCTYPE html>
<html xmlns="http://www.w3c.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head/>
<h:body>
<h:form>
<p:panelGrid columns="1">
<p:commandButton value="Save" actionListener="#{runtimeUpdatesBean.save}" />
<p:panel id="panela">
<h:outputText value="#{runtimeUpdatesBean.texta}"/>
</p:panel>
<p:panel id="panelb">
<h:outputText value="#{runtimeUpdatesBean.textb}"/>
</p:panel>
</p:panelGrid>
</h:form>
</h:body>
</html>
The bean:
package com.glasses.primework;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import org.primefaces.context.RequestContext;
#ManagedBean
#SessionScoped
public class RuntimeUpdatesBean {
private String texta;
private String textb;
private boolean outcome;
public String getTexta() {
texta += "a";
System.out.println("RuntimeUpdatesBean.getTexta() = " + texta);
return texta;
}
public String getTextb() {
textb += "b";
System.out.println("RuntimeUpdatesBean.getTextb() = " + textb);
return textb;
}
public void save() {
RequestContext requestContext = RequestContext.getCurrentInstance();
if(outcome) {
System.out.println("RuntimeUpdatesBean.save() = update panela");
requestContext.update("panela");
outcome = false;
} else {
System.out.println("RuntimeUpdatesBean.save() = update panelb");
requestContext.update("panelb");
outcome = true;
}
}
}
Well the problem is the ID of the component that you are referring.
In JSF when you place a component inside h:form (or Some Primefaces components like TabView), that component's Id will be generated based on the h:form id too.
Here is the Example:
<h:form id="panelaForm">
<p:panel id="panela">
....
</p:panel>
</h:form>
In the above case your p:panel's id will be generated as panelaForm:panela.
In your case since you haven't provided any ID for h:form a dynamic id will be attached like for example j_xyz:panela(you can see it using you browser's Inspect Element).
So If you wan to access p:panel with Id panela inside the same h:form then no need to attach the form Id.
But If you want to access the p:panel outside h:form then you need to attach the h:form id to access it.
Solution to you problem is: use an custom ID to your h:form (which is a best practice by the way..) and access the p:panel by attaching that form ID.
<h:form id="panelaForm">
<p:panel id="panela">
....
</p:panel>
</h:form>
And in Managed bean use:
RequestContext.getCurrentInstance().update("panelaForm:panela");
I'm the new guy here (Java EE) however below solution works for me:
<!DOCTYPE html>
<html xmlns="http://www.w3c.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head/>
<h:body>
<h:form id="form">
<p:panelGrid columns="1">
<p:commandButton value="Save" actionListener="#{runtimeUpdatesBean.save}" update=":form" />
<p:panel id="panela">
<h:outputText value="#{runtimeUpdatesBean.texta}"/>
</p:panel>
<p:panel id="panelb">
<h:outputText value="#{runtimeUpdatesBean.textb}"/>
</p:panel>
</p:panelGrid>
</h:form>
</h:body>
I am building a simple component in JSF (Mojarra 2.1.x) where I have to access parent ui components to update them, currently I'm using binding to achieve this, but it only works as long as I don't use the component more than once on the same page.
So I need a solution that would allow me to use the component multiple times on same page.
In the following code I'm updating commandButton with binding="#{msButton}" and panel with binding="#{msPanel}":
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:component xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:layout="http://sterz.stlrg.gv.at/jsf/layout"
xmlns:p="http://primefaces.org/ui">
<cc:interface>
<cc:attribute name="controller" required="true" />
<cc:attribute name="converter" required="true" />
</cc:interface>
<cc:implementation>
<p:commandButton id="msButton#{cc.attrs.controller.class.getSimpleName()}" binding="#{msButton}" value="#{msg.mehr} (#{cc.attrs.controller.itemList.size()})" type="button" />
<p:overlayPanel id="msOverlayPanel" for=":#{msButton.clientId}" hideEffect="fade" my="right top" at="right bottom">
<p:panel id="msPanel#{cc.attrs.controller.class.getSimpleName()}" binding="#{msPanel}" styleClass="ui-panel-fit">
<ui:repeat id="repeat" value="#{cc.attrs.controller.itemList}"
var="item">
<p:commandButton id="removeButton"
actionListener="#{cc.attrs.controller.removeItem(item)}"
icon="ui-icon-trash" update=":#{msPanel.clientId} :#{msButton.clientId}" ajax="true"
process="#this" disabled="#{cc.attrs.controller.itemList.size() == 1}"/>
<p:selectBooleanButton id="value1" value="#{item.exclude}"
offLabel="und" onLabel="und nicht" style="width:80px;">
<p:ajax event="change" process="#this" />
</p:selectBooleanButton>
<p:autoComplete converter="#{cc.attrs.converter}"
readonly="#{cc.attrs.readonly}" value="#{item.from}"
dropdown="true"
completeMethod="#{cc.attrs.controller.autocomplete}" var="gp"
itemLabel="#{gp.displayName}" itemValue="#{gp}">
<p:ajax event="itemSelect" process="#this" />
</p:autoComplete>
<h:outputText value=" #{msg.bis} " />
<p:autoComplete converter="#{cc.attrs.converter}"
readonly="#{cc.attrs.readonly}" value="#{item.to}" dropdown="true"
completeMethod="#{cc.attrs.controller.autocomplete}" var="gp"
itemLabel="#{gp.displayName}" itemValue="#{gp}">
<p:ajax event="itemSelect" process="#this" />
</p:autoComplete>
<br />
</ui:repeat>
<hr />
<p:commandButton id="addButton" actionListener="#{cc.attrs.controller.addItem}"
icon="ui-icon-plus" value="#{msg.zufuegen}" update="#parent :#{msButton.clientId}"
ajax="true" process="#this"/>
</p:panel>
</p:overlayPanel>
</cc:implementation>
</ui:component>
Any help is much apprecieted.
The solution is to use the faces component bean
#FacesComponent("com.xxx.MultiselectorIdComponent")
public class MultiselectorIdComponent extends UINamingContainer
{
UIComponent msPanel;
// getters/settter and other ui compunents
}
tell the component interface what faces component bean to use
<cc:interface componentType="com.xxx.MultiselectorIdComponent">
bind the JSF component to the one in the faces component bean
<p:panel binding="#{cc.msPanel}"/>
and to access the components, for example to update the component, we use the binding
<p:commandButton value="My Button" update=":#{cc.msPanel.clientId}"/>
ALSO:
A good practice is to use a parent container (like <div>) with the following ID
<div id="#{cc.clientId}">
Hope this helps,
Regards
You could target the components for update by their style class
<p:commandButton styleClass="msButton" ... />
<p:panel styleClass="msPanel" ... />
And I guess you update them from addButton, which would look like this
<p:commandButton id="addButton" update="#(.msButton, .msPanel)" ... />
It should have no problems working with many cc instances on the same page.
UPDATE
You can try with update="#composite", which should refresh the whole custom component. Or, the cumbersome update="#parent #parent:#parent:#parent:#child(1)" which should target panel and button respectively. Or update="#parent #parent:#parent:#previous", which should do the same. Take a look at chapter 4.3 in Primefaces User Guide for more examples and supported keywords.
I solve similar problem by common bean "bindingBean" stored in ViewContext. BindingBean holds all binded component in internal MAP of component records. The key of hash map is an EL expression - it is factory of the component. EL is used for components creating. Components are hold in records stored in MAP in bind bean.
Example:
<h:panel binding="#{bindBean.get('msPanelFactory.getPanel()').component}"/>
Record:
public class BindingRecord {
private Object component;
private String compExpr;
public BindingRecord(String compExpr) {
this.compExpr = compExpr;
}
public Object getComponent() {
if (component == null) {
component = getValueExprValue("#{" + compExpr + "}");
}
return component;
}
public void setComponent(Object component) {
this.component = component;
}
public Object getStoredComponent() {
return component;
}
}
Binding bean:
public class BindingBean {
private final Map<String, BindingRecord> bindingMap = new HashMap<>();
public BindingRecord get(String key) {
BindingRecord result = bindingMap.get(key);
if (result == null) {
result = new BindingRecord(key);
bindingMap.put(key, result);
}
return result;
}
}
In you case you can use:
<h:panel binding="#{bindingBean.get('panelFactory.create()').component}"/>
Inside composite component you can use trick - Pass component name by composite parameter:
<h:panel binding="#{bindingBean.get('panelFactory.create('.concate(cc.attrs.componentName).concate(')')).component}"/>
I have a UI include that have a dynamic content , this content will be loaded when I press in a certain command link. the problem is when I press the command link the UI include is loaded without the Javascript created the PrimeFace:
could this problem be related to #Resource Dependency.
This my controller:
#Named
#ViewScoped
public class Test {
private String value;
private List<String> options=new ArrayList<>();
private String url="";
public String changeUrl(){
url="/snippets/test2.xhtml";
return "#";
}
#PostConstruct
public void init(){
options.add("test 1");
options.add("test 2");
options.add("test 3");
options.add("test 4");
}
//getter and setter
}
and this the xhtml page:
<h:form>
text
<ui:include src="#{test.url}" />
<p:commandLink action="#{test.changeUrl()}" value="submit" process="#this" update="#form"/>
</h:form>
And this the included Page:
<ui:composition>
<h:form>
<p:selectOneMenu value="#{test.value}">
<f:selectItems value="#{test.options}"/>
</p:selectOneMenu>
</h:form>
</ui:composition>
Two problems here: the ui:composition declaration in your included page is missing all of the required namespace declarations, and you are embedding a form within another form.
Your included page should look like this:
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<p:selectOneMenu value="#{test.value}">
<f:selectItems value="#{test.options}"/>
</p:selectOneMenu>
</ui:composition>
This question already has answers here:
How to dynamically add JSF components
(3 answers)
Closed 6 years ago.
A click on a commandButton should trigger an action in a ManagedBean: to add a new "outputText" component to the current page.
The overall idea is to have the page changed dynamically with user action, with server side action because new elements added to the page need data from a db to be laid out.
-> How do I add a component to the page from a managed bean in jsf / primefaces? Let's say that the elements should be added in an existing div like:
<div id="placeHolder">
</div>
(this div could be changed to a jsf panel if needs be)
Note: if alternative methods are better to achieve the same effect I'd be glad to learn about them.
I'll provide you another solution apart from the one you posted. Basically it has a List of given outputs, which is increased everytime the button is pushed. That should render exactly the same DOM tree as the solution you stated:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>Tiles</title>
<h:outputStylesheet name="css/320andup_cle.css" />
</h:head>
<h:body>
<h:form>
<h:commandButton actionListener="#{bean.createNewTile}" title="new"
value="new" />
</h:form>
<h:panelGroup layout="block" id="tiles">
<ui:repeat var="str" value="#{bean.strings}">
<h:panelGroup>
<h:outputText styleClass="tile" value="#{str}" />
</h:panelGroup>
</ui:repeat>
</h:panelGroup>
</h:body>
</html>
#ManagedBean
#SessionScoped
public class Bean {
List<String> strings = new ArrayList<String>();
public List<String> getStrings() {
return strings;
}
public void createNewTile() {
strings.add("output");
}
}
Apart from being much simpler IMHO, it has a main advantage: it doesn't couple your server side code to JSF implicit API. You can change the #ManagedBean annotation for #Named if you want it to be a CDI managed bean.
The solution:
This is a jsf page with a button creating a new div each time it is clicked:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>Tiles</title>
<h:outputStylesheet name="css/320andup_cle.css" />
</h:head>
<h:body>
<h:form>
<h:commandButton actionListener="#{bean.createNewTile()}" title="new" value="new"/>
</h:form>
<h:panelGroup layout="block" id="tiles">
</h:panelGroup>
</h:body>
</html>
The Managed Bean:
#Named
#SessionScoped
public class Bean implements Serializable {
private UIComponent found;
public void createNewTile() {
HtmlPanelGroup div = new HtmlPanelGroup();
div.setLayout("block");
HtmlOutputText tile = new HtmlOutputText();
tile.setValue("heeeeeRRRRRRRRRRRRRR ");
tile.setStyleClass("tile");
div.getChildren().add(tile);
doFind(FacesContext.getCurrentInstance(), "tiles");
found.getChildren().add(div);
}
private void doFind(FacesContext context, String clientId) {
FacesContext.getCurrentInstance().getViewRoot().invokeOnComponent(context, clientId, new ContextCallback() {
#Override
public void invokeContextCallback(FacesContext context,
UIComponent component) {
found = component;
}
});
}
}
See this app built with this logic of dynamically generated components: https://github.com/seinecle/Tiles
I think I have run into a bug in Mojarra 2.1.0. Maybe I missed something but damned if I can see it.
I rely a lot of #ViewScoped beans to save state whilst the browser does a lot of AJAX to the server. I find when I use certain tags, the #ViewScoped bean starts getting re-instantiated when it shouldn't be. Here is my test case backing bean:
/*
* TestStuff.java
*/
package test;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.ActionEvent;
/**
* Backing bean for test.xhtml -- working out AJAX/SVG connection
*
*/
#ManagedBean
#ViewScoped
public class TestStuff implements Serializable {
private int counter = 0;
public TestStuff() {
log("TestStuff(): {0}", this);
}
public String getRandomNumber() {
int i = (int) (Math.random() * 1000000.0);
return String.format("%d", i);
}
public int getCounter() { return counter; }
public List<String> getStuff() {
return Arrays.asList("big", "bad", "wolf");
}
public void pushButton(ActionEvent evt) {
log("TestStuff.pushButton({0}): {1}",
new Object[] { evt, ++counter });
}
}
And here is the JSF Facelets page that uses it:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head>
<title>Test Page</title>
</h:head>
<h:body>
<h1>Test Page</h1>
<p>If you are reading this text, the server
is not properly configured.</p>
<ui:composition id="compRoot" template="/template5.xhtml">
<ui:define name="content">
<h1>Test</h1>
<h:form id="formTest">
<p:commandButton value="Do Me"
actionListener="#{testStuff.pushButton}"
update="testUpdate" />
<p:panel id="testUpdate" >
<h:outputText value="Random output is: " />
<h:outputText
value="#{testStuff.randomNumber}"
/>
<h:outputText value=" Counter is: "/>
<h:outputText
value="#{testStuff.counter}"
/>
</p:panel>
<h:panelGrid columns="5" border="1" >
<c:forEach items="#{testStuff.stuff}" var="x">
<h:outputText value="#{x}" />
</c:forEach>
</h:panelGrid>
</h:form>
</ui:define>
</ui:composition>
</h:body>
</html>
So here is what goes wrong. When you click on the "Do Me" command button, a new instance of the backing bean gets created each time, just as if it were a #RequestScoped bean. I can see this via the log() call in the constructor.
If you change the bean to #SessionScoped, this doesn't happen. You get one instance of the bean no matter how many times the button is clicked.
HOWEVER -- if you leave it as #ViewScoped, and you take out the c:foreach element and its content, it now no longer re-instantiates the bean each click. In other words it now works as expected.
Is this a mojarra bug or am I doing something wrong here?
This is a known "bug": issue 1665. It's a chicken-egg issue with regard to partial state saving.
In your case, however, you could also just use <ui:repeat>.
<ui:repeat value="#{testStuff.stuff}" var="x">
<h:outputText value="#{x}" />
</ui:repeat>
Your best bet is to try to avoid JSTL tags when using #ViewScoped. The only alternative is to disable partial state saving by a context param in web.xml:
<context-param>
<param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>false</param-value>
</context-param>
But it makes the views more memory hogging.