Can't update components created dynamically - jsf

Using Primefaces I pass to a bean the id of the component that the user just clicked. I want the bean to change the style of this component. The relevant part of the bean:
public void passIdClicked() {
FacesContext context = FacesContext.getCurrentInstance();
Map map = context.getExternalContext().getRequestParameterMap();
String idClicked = (String) map.get("idClicked");
UIComponent tile = FindComponent.doFind(context, idClicked);
Iterator<UIComponent> it = tile.getChildren().iterator();
UIComponent comp = it.next();
if (comp.getId().equals(idClicked)) {
HtmlOutputText labelDone = (HtmlOutputText) comp;
labelDone.setValue("heyyyyyyyyy");
labelDone.setStyleClass("myStyleClass");
}
}
The modifications are not appearing on screen. What am I missing?
[EDIT] UI Code:
(additional components get created in the "tiles" panelGroup dynamically in an initialization phase. The passIdClicked method modifies one of these components - the one clicked by the user.)
<h:head>
</h:head>
<h:body>
<h:form id="form">
<h:commandButton actionListener="#{bean.createNewTile()}" title="new" value="new"/>
<p:remoteCommand name="sendNameClicked" actionListener="#{bean.passIdClicked}"/>
</h:form>
<h:panelGroup layout="block" id="tiles">
</h:panelGroup>
<script type="text/javascript">
$(document).ready(function() {
$('.tile').click(function() {
sendNameClicked([{
name: 'idClicked',
value: $(this).attr('id')
}]);
});
});
</script>
</h:body>

#kocko answer is absolutely correct, but since you are using Primefaces you can also do it programmatically:
RequestContext.getCurrentInstance().update(comp.getClientId());

You will have to refresh the component(s), so that the styleClass change takes place.
You can, either:
Refresh the whole page.
For example:
<f:ajax render="#all" />
Or
Refresh a particular component:
For example:
<p:someComponent id="componentId">
....
</p:someComponent>
...
<f:ajax render="componentId" />

Related

How to reset p:dialog together with the disable-state of the contained components?

Primefaces 4.0
I need to reset the initial disable-state of components contained in a p:dialog.
The following simplified example shows the problem:
XTML:
<p:dialog header="header" widgetVar="dialog" appendTo="#(body)"
modal="true" resizable="false">
<h:form id="form">
<p:inputText value="#{bean.text}" id="text" />
<p:commandButton value="Disable InputText"
action="#{bean.disableInputText}" />
<p:commandButton value="Cancel"
action="#{bean.cancelDialog}"
process="#this"
update="#form" immediate="true">
<p:resetInput target="#form"/>
</p:commandButton>
</h:form>
</p:dialog>
ManagedBean:
#ViewScoped
public class Bean {
public void disableText() {
final FacesContext context = FacesContext.getCurrentInstance();
final UIViewRoot root = context.getViewRoot();
final UIComponent component = root.findComponent(":text");
if (uiComponent instanceof InputText) {
((InputText) uiComponent).setDisabled(true);
}
}
public void cancel() {
// reset disable-state of the disable-state of all components in a generic way.
}
}
While using the dialog the p:inputText element can be disabled. If the dialog was canceled and opened again, inputText should not disabled. The initial state should have been restored. Please note that this example is simplified and i am looking for a general solution that also works with a formular with 10+ input elements.
The general (and broadest) solution
For a general solution you can use the state saving functionality that is available in Java Server Faces. Using your code example as a base (with some minor changes to clean things up), here is an example that uses state saving and restores the previous state of the component;
<?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>Disable Test</title>
</h:head>
<h:body>
<p:dialog header="header" widgetVar="dialog" appendTo="#(body)" modal="true" resizable="false">
<h:form id="form">
<p:inputText value="#{disableTestBean.text}" id="text"/>
<p:commandButton value="Disable InputText" action="#{disableTestBean.onDisable}" update="#form" />
<p:commandButton value="Cancel" action="#{disableTestBean.onCancel}" update="#form" onsuccess="PF('dialog').hide()" />
</h:form>
</p:dialog>
<button onclick="PF('dialog').show()">Open</button>
</h:body>
</html>
To actually see the reset happening, get rid of the onsuccess attribute from the second commandButton - as it currently closes the dialog.
#Data
#Named
#ViewScoped
public class DisableTestBean implements Serializable {
private String text;
private Object prevState;
private UIComponent findComponent(String where) {
final FacesContext context = FacesContext.getCurrentInstance();
final UIViewRoot root = context.getViewRoot();
return (UIComponent) root.findComponent(where);
}
public void onDisable() {
final InputText component = (InputText) findComponent(":form:text");
component.setDisabled(false);
component.setValue("");
prevState = component.saveState(FacesContext.getCurrentInstance());
component.setValue("meh");
component.setDisabled(true);
}
public void onCancel() throws IOException {
final InputText component = (InputText) findComponent(":form:text");
component.restoreState(FacesContext.getCurrentInstance(), prevState);
}
}
The example targets one specific input component. However, if you need to handle multiple components, you can easily use a loop to accomplish what you want in a general way.
As a demonstration I not only reset the disabled state in the backing bean above, but also the value (content) of the input component. This demonstrates you how you can actually reset the complete state of the component (not only a single attribute or value). So the solution is broad and very general.
The second more direct approach
The second approach is to do what #Kukeltje is hinting at in his comments above. Use the disabled= attribute on the input components and have a backing bean value that just changes the value to false when you press Cancel. This is not as general and wont work for everything else, but it will do the job in your particular use case. If you are only interested in the disabled state it's probably even the prefered way of doing it. If you want an example of that as well, I can extend this answer - just let me know.

Controlling component from backing bean

I want to 'update' a component with a new component in the bean.
XHTML
<composite:interface>
<composite:attribute name="rootKey" required="true" />
<composite:attribute name="id" required="true" />
</composite:interface>
<composite:implementation>
<rich:panel id="#{cc.attrs.id}" binding="#{myBean.customPanel}"/>
<a4j:jsFunction name="createPanels"
action="#{myBean.createPanels}"
render="#{cc.attrs.id}">
<a4j:param name="rootId" assignTo="#{myBean.rootId}"/>
<a4j:param name="rootKey" assignTo="#{myBean.rootKey}"/>
</a4j:jsFunction>
<script type="text/javascript">
/* <![CDATA[ */
jQuery(document).ready(function() {
createPanels('#{cc.attrs.id}','#{cc.attrs.rootKey}');
});
/*]]> */
</script>
</composite:implementation>
</ui:composition>
Bean
private UIPanel rootPanel;
public void setCustomPanel(UIPanel panel) {
rootPanel = panel;
}
public UIPanel getCustomPanel() {
return rootPanel;
}
public void createPanels() {
//try #1 : Adding new panels as children
rootPanel.getChildren().add((UIPanel)createPanels(rootId,rootKey));
//try #2 : A new Panel component
rootPanel = (UIPanel)createPanels(rootId,rootKey);
...
rootPanel.setId(rootId); // this ID is the same as the 'placeholder' panelId
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().add(rootPanel.getId()); // the rerender should also already be done in the XHTML js function.
}
With the debugger i see the rootPanel changes into the new panel component, but not in the view.
What do i miss?
What i try in short: Generating dynamically components as children for the panel component in the xhtml view. The generation needs the 'rootKey' param for generating the right set.
Using:
JSF Mojarra 2.1.19
Richfaces
The solution for me was to rerender te parent, not the actual component.
Also adding the new element children as children to the bound component.
XHTML : Added a h:panelgroup around the place holder.
<h:panelGroup id="#{cc.attrs.id}_parent">
<rich:panel id="#{cc.attrs.id}" binding="#{myBean.panel}"/>
</h:panelGroup>
<a4j:jsFunction name="createPanels"
action="#{myBean.createPanels}"
render="#{cc.attrs.id}_parent">
<a4j:param name="rootId" assignTo="#{myBean.rootId}"/>
<a4j:param name="rootKey" assignTo="#{myBean.rootKey}"/>
</a4j:jsFunction>
BEAN : try #1 was the right one.
public void createPanels() {
UIPanel newPanel = (UIPanel)createPanels(rootId,RootKey);
if(newPanel.getChildCount() >= 1){
rootPanel.getChildren().addAll(newPanel.getChildren());
rootPanel.setStyleClass(newPanel.getStyleClass());
rootPanel.setHeader(newPanel.getHeader());
}
}

PostConstruct methos called again with richfaces 4.2 , works fine with myfaces

I am seeing different behaviors of and in a page containing multiple forms.
Here is my backing bean:
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class MultiFormBean
{
String inputText1 = "";
String inputText2 = "";
#PostConstruct
public void initializeBean(){
System.out.println("PostConstruct Called ------------------");
}
public String getInputText1()
{
return inputText1;
}
public void setInputText1(String inputText1)
{
this.inputText1 = inputText1;
}
public String getInputText2()
{
return inputText2;
}
public void setInputText2(String inputText2)
{
this.inputText2 = inputText2;
}
public void doSubmit1() {
inputText2 = inputText1;
}
public void doSubmit2() {
inputText1 = inputText2;
}
}
When i use the following xhtml , clicking Submit1 and Submit2 any number of times won't call #PostConstruct more than once:
<h:body>
<h:form id="firstForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget1"/>
<h:inputText id="first_input" value="#{multiFormBean.inputText1}"/>
<h:commandButton id="click1" action="#{multiFormBean.doSubmit1}" value="submit1" type="submit"
onclick="javascript:jsf.ajax.request(this, event, {execute:'firstForm', render:'renderTarget1 secondForm'}); return false;">
</h:commandButton>
</h:form>
<h:form id="secondForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget2"/>
<h:inputText id="second_input" value="#{multiFormBean.inputText2}"/>
<h:commandButton id="click2" action="#{multiFormBean.doSubmit2}" value="submit2" type="submit"
onclick="javascript:jsf.ajax.request(this, event, {execute:'secondForm', render:'renderTarget2 firstForm'}); return false;">
</h:commandButton>
</h:form>
</h:body>
But the following xhtml would call #PostConstruct more than once:
<h:body>
<h:form id="firstForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget1"/>
<h:inputText id="first_input" value="#{multiFormBean.inputText1}"/>
<a4j:commandButton id="click1" action="#{multiFormBean.doSubmit1}" value="submit1" type="submit" execute="#form" render="renderTarget1,secondForm"/>
</h:form>
<h:form id="secondForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget2"/>
<h:inputText id="second_input" value="#{multiFormBean.inputText2}"/>
<a4j:commandButton id="click2" action="#{multiFormBean.doSubmit2}" value="submit2" type="submit" execute="#form" render="renderTarget2,firstForm"/>
</h:form>
</h:body>
Please can anyone help me use the <a4j:commandButton> instead of <h:commandButton>
Also i see that i cannot call the method doSubmit2() with a4j commandButton
I think that problem here is in bug inside JSF2 and Richfaces4. From 4 version Richfaces started using JSF embedded ajax capabilities. And There is a bug with using multiple forms on page with ajax requests. The problem there that richfaces renders special hidden input with the id of currently rendered view state. This id is changed when new view is rendered. And it is also submitted with every request to show that it belongs to some specific view. So when you have multiple forms on the same page after first ajax request the view state is getting the wrong place and it can be not submitted again second time. Sometimes behavior looks like very very wierd with no logical description.
PostConstruct is called twice because server thinks that two requests belong to different views(view state is not sumbitted) and as far as bean is view scoped it is created twice. After clicking aroung ajax can completelly stop working with this because server woukd not recognize the view(probably what you see when you can not click second submit button).
In the first place I recommend you to use latest available version of JSF and Richfaces. This bug (and many more) may be already fixed there.

how to add a component to the page from a managed bean in jsf / primefaces [duplicate]

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

How to add a message to a specific component from JSF backing bean

I have an h:inputText and an h:message connected to it:
<h:inputText id="myText" value="#{myController.myText}" />
<a4j:outputPanel>
<h:message for="myText" .../>
</a4j:outputPanel>
I want to send a message to it from java, in a manner like:
FacesContext.getCurrentInstance().addMessage(arg0, arg1);
which is sent to h:messages, but to a specific id in a specific form.
How can I do this? (Without implementing validation bean or validation method - meaning without throwing validation exception).
You need to provide the so called client id, which you'll find on UIComponent.
The following is a quick example of how to use this.
Consider the following bean:
#ManagedBean
#RequestScoped
public class ComponentMsgBean {
private UIComponent component;
public UIComponent getComponent() {
return component;
}
public void setComponent(UIComponent component) {
this.component = component;
}
public String doAction() {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(component.getClientId(), new FacesMessage("Test msg"));
return "";
}
}
being used on the following Facelet:
<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:ui="http://java.sun.com/jsf/facelets"
>
<h:body>
<h:form>
<h:outputText id="test" value="test component" binding="#{componentMsgBean.component}"/>
<h:message for="test"/>
<h:commandButton value="click me" action="#{componentMsgBean.doAction}" />
</h:form>
</h:body>
</html>
This will add a Faces message with content "Test msg" for the outputText component used in the example.
Another way to do that is: give an ID to the form, like "form1", then, when add the message, the clientId is "form1:test".

Resources