This question already has answers here:
Find component by ID in JSF
(5 answers)
Closed 1 year ago.
I tried to do in a ManagedBean:
UIComponent a = FacesContext.getCurrentInstance().getViewRoot().findComponent("a:b");
I checked with the chrome browser inspector, and the component has id "a:b". But debugging the code, I get the UIComponent equal to null.
I tried with all the combinations: ":a:b", ":b", "b", "\\:a\\:b"...
I'm using Primefaces 3.4.1 with Mojarra 2.1.7
UPDATE: I also tried as suggested in the comments:
UIComponent container = FacesContext.getCurrentInstance().getViewRoot().findComponent("a");
UIComponent b = ((UIComponentBase) container).findComponent("b");
container is not null, but b is null
Solved inspired to Abbas Gadhia's answer:
public class JsfUtil {
public static UIComponent findComponent(String id) {
FacesContext context = FacesContext.getCurrentInstance();
UIComponent root = context.getViewRoot();
UIComponent res = root.findComponent(id);
return res;
}
public static UIComponent findSubComponent(final String id, UIComponent container) {
FacesContext context = FacesContext.getCurrentInstance();
if (container == null) {
container = context.getViewRoot();
}
String idContainer = container.getId();
if (id.equals(idContainer)) {
return container;
}
final UIComponent[] found = new UIComponent[1];
VisitContext visitContext = VisitContext.createVisitContext(context);
container.visitTree(visitContext, new VisitCallback() {
#Override
public VisitResult visit(
VisitContext context,
UIComponent component
) {
String idComponent = component.getId();
if (id.equals(idComponent)) {
found[0] = component;
return VisitResult.COMPLETE;
}
return VisitResult.ACCEPT;
}
});
return found[0];
}
}
Usage:
UIComponent container = JsfUtil.findComponent("containerId");
UIComponent element = JsfUtil.findSubComponent("lastPartOfElementId", container);
Related
This is how I'm rendering my composite component inside a loop, it works, but when I switch to edit mode and sumbmit new values I can't retrieve them from the InputText.
#FacesComponent("customComponent")
public class CustomComponent extends UIInput implements NamingContainer, Serializable {
private static final long serialVersionUID = 1L;
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
private UIComponent component;
private HtmlInputText inputTextValue;
#Override
public void encodeBegin(FacesContext context) throws IOException {
AttributeObject attrObject = (AttributeObject) getAttributes().get("value");
Boolean enableInput = (Boolean) getAttributes().get("enableInput");
if (attrObject.getAttributeValue() != null) {
if (attrObject.getAttributeDescriptor().getDataType() == DataTypeConstants.TEXT && enableInput) {
InputText inputText = new InputText();
inputText.setRequired(true);
inputText.setValueExpression("binding",
createValueExpression("#{searchController.myComponent}", UIComponent.class));
inputText.setId("editableTextId");
inputText.encodeAll(context);
inputText.setParent(this);
component = inputText;
} else if (attrObject.getAttributeDescriptor().getDataType() == DataTypeConstants.TEXT
&& enableInput == false) {
OutputLabel outputLabel = new OutputLabel();
outputLabel.setValue(attrObject.getAttributeValue());
outputLabel.encodeAll(context);
outputLabel.setId("nonEditatbleId");
component = outputLabel;
}
}
}
private ValueExpression createValueExpression(String valueExpression, Class<?> valueType) {
FacesContext facesContext = FacesContext.getCurrentInstance();
return facesContext.getApplication().getExpressionFactory()
.createValueExpression(facesContext.getELContext(), valueExpression, valueType);
}
Ok I think I found what caused all that mad performance problems. I did some logic inside a getter and because that getter was getting called multiple times that caused performance issues.
I want to find some UIComponent from managed bean by the id that I have provided.
I have written the following code:
private UIComponent getUIComponent(String id) {
return FacesContext.getCurrentInstance().getViewRoot().findComponent(id) ;
}
I have defined a p:inputTextarea as:
<p:inputTextarea id="activityDescription" value="#{adminController.activityDTO.activityDescription}" required="true" maxlength="120"
autoResize="true" counter="counter" counterTemplate="{0} characters remaining." cols="80" rows="2" />
Now if a call to the method as getUIComponent("activityDescription") it is returning null, but if I call it as getUIComponent("adminTabView:activityForm:activityDescription") then I can get the org.primefaces.component.inputtextarea.InputTextarea instance.
Is there any way to get the component with only the id i.e., "activityDescription" not the absolute id i.e., "adminTabView:activityForm:activityDescription"?
You can use the following code:
public UIComponent findComponent(final String id) {
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
final UIComponent[] found = new UIComponent[1];
root.visitTree(new FullVisitContext(context), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent component) {
if (component != null
&& id.equals(component.getId())) {
found[0] = component;
return VisitResult.COMPLETE;
}
return VisitResult.ACCEPT;
}
});
return found[0];
}
This code will find only the first component in the tree with the id you pass. You will have to do something custom if there are 2 components with the same name in the tree (this is possible if they are under 2 different naming containers).
I try this code, and it's help:
private static UIComponent getUIComponentOfId(UIComponent root, String id){
if(root.getId().equals(id)){
return root;
}
if(root.getChildCount() > 0){
for(UIComponent subUiComponent : root.getChildren()){
UIComponent returnComponent = getUIComponentOfId(subUiComponent, id);
if(returnComponent != null){
return returnComponent;
}
}
}
return null;
}
Thanks
Maybe it's not possible. The FacesContext.getCurrentInstance().getViewRoot().findComponent(id) method returns only one UIComponent. The ViewRoot is constructed as a tree so if you have two forms in the view, each one with a component with id="text", they will have it's parent components added to the id so they won't conflict. If you put the two id="text" components within the same form, you will have java.lang.IllegalStateException thrown.
If you want to find all components with the searched id, you could write a method that implements:
List<UIComponent> foundComponents = new ArrayList();
for(UIComponent component: FacesContext.getCurrentInstance().getViewRoot().getChildren()) {
if(component.getId().contains("activityDescription")){
foundComponents.add(component);
}
}
Or if you want to find the first occurrence:
UIComponent foundComponent;
for(UIComponent component: FacesContext.getCurrentInstance().getViewRoot().getChildren()) {
if(component.getId().contains("activityDescription")){
foundComponent = component;
break;
}
}
Just put prependId="false" to your form in which this textarea is.
Yes, in all parent components which are NamingContainers you have to add attribute prependId="false" - it will works in <h:form> for sure and should work in others. If it is not possible to set it via attribute in .xhtml file you have to set such value programaticaly.
Suggestion after question's author comment:
If there is not such attribute in components that you are using try write find method like this:
private UIComponent findComponent(String id, UIComponent where) {
if (where == null) {
return null;
}
else if (where.getId().equals(id)) {
return where;
}
else {
List<UIComponent> childrenList = where.getChildren();
if (childrenList == null || childrenList.isEmpty()) {
return null;
}
for (UIComponent child : childrenList) {
UIComponent result = null;
result = findComponent(id, child);
if(result != null) {
return result;
}
return null;
}
Next just invoke
UIComponent iamLookingFor = findComponent(myId, FacesContext.getCurrentInstance().getViewRoot());
That will help?
My FaceletContext I try to get with
FaceletContext faceletContext = (FaceletContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
is always null. What is going wrong here?
Actually what I try to do is to add a composite component dynamically from bean.
Update:
To be more clear. I have the following bean:
#Named
#SessionScoped
public class GroupComponentTreeBuilder implements Serializable {
public UIComponent getGroupTree() {
UIComponent parent1 = UIComponent.getCurrentComponent(FacesContext.getCurrentInstance());
return includeCompositeComponent(parent1, "group", "elementgroup.xhtml", "myNewSuperId");
}
public static UIComponent includeCompositeComponent(UIComponent parent, String libraryName, String resourceName, String id) {
FacesContext context = Faces.getContext();
Application application = context.getApplication();
FaceletContext faceletContext = Faces.getFaceletContext();
Resource resource = application.getResourceHandler().createResource(resourceName, libraryName);
UIComponent composite = application.createComponent(context, resource);
composite.setId(id);
UIComponent implementation = application.createComponent(UIPanel.COMPONENT_TYPE);
implementation.setRendererType("javax.faces.Group");
composite.getFacets().put(UIComponent.COMPOSITE_FACET_NAME, implementation);
parent.getChildren().add(composite);
parent.pushComponentToEL(context, composite); // This makes #{cc}
// available.
try {
faceletContext.includeFacelet(implementation, resource.getURL());
} catch (IOException e) {
throw new FacesException(e);
} finally {
parent.popComponentFromEL(context);
}
return composite;
}
}
And I want to bind the loaded bean to a panelGroup :
<ui:fragment>
<h:panelGroup binding="#{groupComponentTreeBuilder.groupTree}" />
</ui:fragment>
The problem is that the FacletContext is null, so i can not call includerFacelet ().
Any suggestions?
The problem was that
FaceletContext.FACELET_CONTEXT_KEY
returned the wrong String key "com.sun.faces.facelets.FACELET_CONTEXT"
but I needed
javax.faces.FACELET_CONTEXT
Thanx Mat :-)
I want to find some UIComponent from managed bean by the id that I have provided.
I have written the following code:
private UIComponent getUIComponent(String id) {
return FacesContext.getCurrentInstance().getViewRoot().findComponent(id) ;
}
I have defined a p:inputTextarea as:
<p:inputTextarea id="activityDescription" value="#{adminController.activityDTO.activityDescription}" required="true" maxlength="120"
autoResize="true" counter="counter" counterTemplate="{0} characters remaining." cols="80" rows="2" />
Now if a call to the method as getUIComponent("activityDescription") it is returning null, but if I call it as getUIComponent("adminTabView:activityForm:activityDescription") then I can get the org.primefaces.component.inputtextarea.InputTextarea instance.
Is there any way to get the component with only the id i.e., "activityDescription" not the absolute id i.e., "adminTabView:activityForm:activityDescription"?
You can use the following code:
public UIComponent findComponent(final String id) {
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
final UIComponent[] found = new UIComponent[1];
root.visitTree(new FullVisitContext(context), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent component) {
if (component != null
&& id.equals(component.getId())) {
found[0] = component;
return VisitResult.COMPLETE;
}
return VisitResult.ACCEPT;
}
});
return found[0];
}
This code will find only the first component in the tree with the id you pass. You will have to do something custom if there are 2 components with the same name in the tree (this is possible if they are under 2 different naming containers).
I try this code, and it's help:
private static UIComponent getUIComponentOfId(UIComponent root, String id){
if(root.getId().equals(id)){
return root;
}
if(root.getChildCount() > 0){
for(UIComponent subUiComponent : root.getChildren()){
UIComponent returnComponent = getUIComponentOfId(subUiComponent, id);
if(returnComponent != null){
return returnComponent;
}
}
}
return null;
}
Thanks
Maybe it's not possible. The FacesContext.getCurrentInstance().getViewRoot().findComponent(id) method returns only one UIComponent. The ViewRoot is constructed as a tree so if you have two forms in the view, each one with a component with id="text", they will have it's parent components added to the id so they won't conflict. If you put the two id="text" components within the same form, you will have java.lang.IllegalStateException thrown.
If you want to find all components with the searched id, you could write a method that implements:
List<UIComponent> foundComponents = new ArrayList();
for(UIComponent component: FacesContext.getCurrentInstance().getViewRoot().getChildren()) {
if(component.getId().contains("activityDescription")){
foundComponents.add(component);
}
}
Or if you want to find the first occurrence:
UIComponent foundComponent;
for(UIComponent component: FacesContext.getCurrentInstance().getViewRoot().getChildren()) {
if(component.getId().contains("activityDescription")){
foundComponent = component;
break;
}
}
Just put prependId="false" to your form in which this textarea is.
Yes, in all parent components which are NamingContainers you have to add attribute prependId="false" - it will works in <h:form> for sure and should work in others. If it is not possible to set it via attribute in .xhtml file you have to set such value programaticaly.
Suggestion after question's author comment:
If there is not such attribute in components that you are using try write find method like this:
private UIComponent findComponent(String id, UIComponent where) {
if (where == null) {
return null;
}
else if (where.getId().equals(id)) {
return where;
}
else {
List<UIComponent> childrenList = where.getChildren();
if (childrenList == null || childrenList.isEmpty()) {
return null;
}
for (UIComponent child : childrenList) {
UIComponent result = null;
result = findComponent(id, child);
if(result != null) {
return result;
}
return null;
}
Next just invoke
UIComponent iamLookingFor = findComponent(myId, FacesContext.getCurrentInstance().getViewRoot());
That will help?
This question already has answers here:
Conversion Error setting value for 'null Converter' - Why do I need a Converter in JSF?
(2 answers)
Closed 6 years ago.
one more time i'm in trouble here. My point is:
In my project i need a converter for (obviously) convert the items from the SelectOneMenu component to a list property in the respective bean. In my jsf page i have:
<p:selectOneMenu id="ddlPublicType" value="#{publicBean.selectedPublicType}" effect="fade" converter="#{publicBean.conversor}" >
<f:selectItems value="#{publicoBean.lstPublicTypes}" var="pt" itemLabel="#{pt.label}" itemValue="#{pt.value}"></f:selectItems>
</p:selectOneMenu>
And my bean is:
#ManagedBean(name = "publicBean")
#RequestScoped
public class PublicBean {
// Campos
private String name; // Nome do evento
private TdPublicType selectedPublicType = null;
private List<SelectItem> lstPublicTypes = null;
private static PublicTypeDAO publicTypeDao; // DAO
static {
publicTypeDao = new PublicTypeDAO();
}
// Construtor
public PublicoBean() {
lstPublicTypes = new ArrayList<SelectItem>();
List<TdPublicType> lst = publicTypeDao.consultarTodos();
ListIterator<TdPublicType> i = lst.listIterator();
lst.add(new SelectItem("-1","Select..."));
while (i.hasNext()) {
TdPublicType actual = (TdPublicType) i.next();
lstPublicTypes.add(new SelectItem(actual.getIdPublicType(), actual.getNamePublicType()));
}
}
// Getters e Setters
...
public Converter getConversor() {
return new Converter() {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// This value parameter seems to be the value i had passed into SelectItem constructor
TdPublicType publicType = null; // Retrieving the PublicType from Database based on ID in value parameter
try {
if (value.compareTo("-1") == 0 || value == null) {
return null;
}
publicType = publicTypeDao.findById(Integer.parseInt(value));
} catch (Exception e) {
FacesMessage msg = new FacesMessage("Error in data conversion.");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
FacesContext.getCurrentInstance().addMessage("info", msg);
}
return publicType;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return value.toString(); // The value parameter is a TdPublicType object ?
}
};
}
...
}
In the getAsObject() method, the value parameter seems to be the value i had passed into SelectItem constructor. But in the getAsString() method, the value also seems to be a string representation of an Id. This parameter shouldn't be of type TdPublicType ? There is anything wrong in my code?
The getAsString() should convert the Object (which is in your case of type TdPublicType) to a String which uniquely identifies the instance, e.g. some ID, so that it can be inlined in HTML code and passed around as HTTP request parameters. The getAsObject() should convert exactly that unique String representation back to the concrete Object instance, so that the submitted HTTP request parameter can be converted back to the original object instance.
Basically (trivial prechecks and exception handling omitted):
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) throws ConverterException {
// Convert Object to unique String representation for display.
return String.valueOf(((TdPublicType) modelValue).getId());
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) throws ConverterException {
// Convert submitted unique String representation back to Object.
return tdPublicTypeService.find(Long.valueOf(submittedValue));
}
Update: you've another problem, you're specifying the value property of TdPublicType class as the item value instead of the TdPublicType instance itself. This way the converter will retrieve the value property instead of the TdPublicType instance in the getAsString(). Fix it accordingly:
<f:selectItems value="#{publicoBean.lstPublicTypes}" var="pt"
itemLabel="#{pt.label}" itemValue="#{pt}"/>
Now the code is working. My error was in the loading method. I was doing this:
// Loading menu
List<TdPublicType> l = daoPublicType.retrieveAll();
Iterator<TdPublicType> i = l.iterator();
while (i.hasNext()) {
TdPublicType actual = (TdPublicType) i.next();
lstMenuPublicType.add(new SelectItem(actual.getIdtPublicType(), actual.getNamePublicType()));
}
But the right way is:
// Loading menu
List<TdPublicType> l = daoPublicType.retrieveAll();
Iterator<TdPublicType> i = l.iterator();
while (i.hasNext()) {
TdPublicType actual = (TdPublicType) i.next();
lstMenuPublicType.add(new SelectItem(actual, actual.getNamePublicType())); // In the first parameter i passed the PublicType object itself not his id.
}
use can use generic converter which will convert the value in the backing bean.
You do not need any casting also.
#FacesConverter(value = "GConverter")
public class GConverter implements Converter{
private static Map<Object, String> entities = new WeakHashMap<Object, String>();
#Override
public String getAsString(FacesContext context, UIComponent component, Object entity) {
synchronized (entities) {
if (!entities.containsKey(entity)) {
String uuid = UUID.randomUUID().toString();
entities.put(entity, uuid);
return uuid;
} else {
return entities.get(entity);
}
}
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String uuid) {
for (Entry<Object, String> entry : entities.entrySet()) {
if (entry.getValue().equals(uuid)) {
return entry.getKey();
}
}
return null;
}
}
Example usage would be
<p:selectOneMenu id="ddlPublicType" value="#{publicBean.selectedPublicType}" effect="fade" converter="GConverter" >
<f:selectItems value="#{publicoBean.lstPublicTypes}" var="pt" itemLabel="#{pt.label}" itemValue="#{pt}"></f:selectItems>
</p:selectOneMenu>