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 :-)
Related
Server: Payara 5.183.
When the converter is used, a NullPointerException is raised because the injected EJB is null (System.out.println prints "null").
It works (injection not null) if I use a workaround used before JSF 2.3: replacement of #FacesConverter by #Name.
Converter:
#FacesConverter(value = "compteConverter", managed = true)
public class CompteConverter implements Converter<CompteBancaire> {
#EJB
private GestionnaireCompte gestionnaireCompte;
#Override
public CompteBancaire getAsObject(FacesContext context, UIComponent component, String id) {
if (id == null || id.isEmpty()) {
return null;
}
try {
System.out.println("*****EJB gestionnaireCompte=" + gestionnaireCompte);
return gestionnaireCompte.getCompte(Long.parseLong(id));
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage("Id de compte invalide"), e);
}
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, CompteBancaire compte) { ... }
Usage of this converter:
<ui:define name="metadata">
<f:metadata>
<f:viewParam name="id" value="#{operations.compte}"
converter="compteConverter"/>
Is it a bug of Mojarra/Payara (managed = true is not working) or can you help me to find my error?
Managed converters don't work by default. To make them work I added a CDI bean annotated by #FacesConfig (for JSF 2.3 to be used) and #ApplicationScoped (it will be a CDI bean with this annotation).
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?
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?
i'm facing a problem with a Converter. In my xhtml file, i have a selectOneMenu with a list of object and i want to set an object in my managedBean.
If my managedBean has #SessionScoped, the object in the managedbean is filled but if the managedeban has #ViewScoped, the converter is never use and my object is null.
how to fix this problem ?
Xhtml :
<p:selectOneMenu value="#{rechercheBean.role}" converter="#{typConverter}">
<f:selectItems id="item" value="#{typBean.roles}" var="r" itemLabel="#{r.valeur}" itemValue="#{r}" />
</p:selectOneMenu>
typConverter :
public class TypConverter implements Converter{
#EJB
private TypFacadeLocal TypBean;
public Object getAsObject(FacesContext facesContext, UIComponent component, String submittedValue) {
if (submittedValue.trim().equals("")) {
return null;
}
else {
try {
Integer id = Integer.parseInt(submittedValue);
Typ typ = new Typ();
typ = TypBean.find(id);
return typ;
}
catch (NumberFormatException exception) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion Error", "Typ non valide"));
}
}
}
public String getAsString(FacesContext facesContext, UIComponent component, Object value) {
if (value == null || value.equals("")) {
return "";
}
else {
return String.valueOf(((Typ) value).getId());
}
}
}
Tx a lot
The problem is the component c:when. With the attribut renderer of the component , there is no problem.
I'm using the PrimeFaces p:autoComplete widget in a search form of my project. The user can choose how many and which form-elements (search parameters) he wants to include so I need to pass an ID to the completeMethod for each of them. I've tried adding onfocus=".." to pass the object to the bean but that only would be activated when the element first is loaded.
My question: How can I pass an attribute to the completeMethod?
XHTML of the element (simple):
<p:autoComplete value="#{filter.value}" label="dynamic search attribute"
completeMethod="#{myBean.complete}" />
The bean (simple):
#Named("myBean")
public class MyController implements Serializable {
public List<String> complete(String query) {
List<String> results = new ArrayList<String>();
// ... code
return results;
}
}
In theory this would seem like the perfect solution:
<p:autoComplete value="#{filter.value}" label="dynamic search attribute"
completeMethod="#{myBean.complete(filter)}" />
And again the bean:
#Named("myBean")
public class MyController implements Serializable {
public List<String> complete(String query, FilterObject o) {
List<String> results = new ArrayList<String>();
// ... database query based on FilterObject o
return results;
}
}
You can set it as an attribute:
<p:autoComplete value="#{filter.value}" label="dynamic search attribute" completeMethod="#{myBean.complete}">
<f:attribute name="filter" value="#{filter}" />
</p:autoComplete>
and get it by UIComponent#getCurrentComponent():
public List<String> complete(String query) {
FacesContext context = FacesContext.getCurrentInstance();
FilterObject o = (FilterObject) UIComponent.getCurrentComponent(context).getAttributes().get("filter");
// ...
}
Alternatively, as that #{filter} appears in your case to be already in the EL scope, you can also leave the <f:attribute> away and get it by evaluating the EL expression programmatically with help of Application#evaluateExpressionGet():
public List<String> complete(String query) {
FacesContext context = FacesContext.getCurrentInstance();
FilterObject o = context.getApplication().evaluateExpressionGet(context, "#{filter}", FilterObject.class);
// ...
}
Or, if it is also a #Named bean, then you can just #Inject it in the parent bean:
#Inject
private FilterObject o;