Hi i trying to set value expresion on some attribute (for example attribute class) of DefaultMenuItem of Primefaces. Something like:
<p:menuitem class="#{view.viewId.endsWith('index.xhtml') ? 'menuitm-hovered' : ''}" value="Home" url="/" />
exaclty these peace
class="#{view.viewId.endsWith('index.xhtml') ? 'menuitm-hovered' : ''}"
but programaticaly. On bean should be something like these:
menuItem.setValueExpression("class", Helper2.createValueExpression("#{view.viewId.endsWith('index.xhtml') ? 'menuitm-hovered' : ''}", Object.class));
DefaultMenuItem dosent have setValueExpression method so im stuck and I dont know how i could do that. If someone could help i will be grateful for help.
As far as i know setValueExpression can be done in GraphicImage (in these case with value attribute)
GraphicImage image = new GraphicImage();
image.setValueExpression("value", Helper2.createValueExpression("#{position.images.link}", Object.class));
Below static method of Helper2 class:
public static ValueExpression createValueExpression(String expression, Class clazz) {
FacesContext fc = FacesContext.getCurrentInstance();
ELContext elContext = fc.getELContext();
ExpressionFactory expFactory = fc.getApplication().getExpressionFactory();
ValueExpression ret = expFactory.createValueExpression(elContext, expression, clazz);
return ret;
}
Try this:
MenuButton menuButton = new MenuButton();
item = new UIMenuItem();
item.setValue(command.getLabel());
item.setActionExpression(Utility.createMethodExp(command.getCommandExpression()));
item.setValueExpression("disabled", Utility.createExpression(command.getDisabledExpression(), Boolean.class));
menuButton.getChildren().add(item);
where Utility.createMethodExpression is a static method (of my class Utility):
public static MethodExpression createMethodExp(String stringExpression) {
FacesContext context = FacesContext.getCurrentInstance();
return context.getApplication().getExpressionFactory().createMethodExpression(Utility.getELContext(),
stringExpression, Void.class, new Class[] {});
}
and Utility.createExpression is
public static ValueExpression createExpression(String stringExpression, Class<?> type) {
FacesContext context = FacesContext.getCurrentInstance();
return context.getApplication().getExpressionFactory().createValueExpression(context.getELContext(),
stringExpression, type);
}
Related
public void populateForm(ComponentSystemEvent event) {
HtmlForm form = (HtmlForm) event.getComponent();
List<String> citiesSource = new ArrayList<String>();
List<String> citiesTarget = new ArrayList<String>();
citiesSource.add("San Francisco");
citiesSource.add("London");
citiesSource.add("Paris");
citiesSource.add("Istanbul");
citiesSource.add("Berlin");
citiesSource.add("Barcelona");
citiesSource.add("Rome");
cities = new DualListModel<String>(citiesSource, citiesTarget);
PickList pickList = new PickList();
pickList.setId("pickList");
pickList.setValue(cities);
pickList.setVar("cities");
pickList.setItemLabel(pickList.getVar());
pickList.setItemValue(cities);
form.getChildren().add(pickList);
}
This was my approach, and output I got is:
But if I add <p:pickList id="pickList" value="#{bean.cities}" var="cities" itemLabel="#{cities}" itemValue="#{cities}" /> in my xhtml, I get this output:
What is going on here?
This is how I solved the PickList issue:
FacesContext context = FacesContext.getCurrentInstance();
ExpressionFactory ef = context.getApplication().getExpressionFactory();
PickList pickList = new PickList();
ValueExpression pickListValue = ef.createValueExpression(
context.getELContext(), "#{ruleManagedBean.cities}",DualListModel.class);
ValueExpression pickListVar = ef.createValueExpression(
context.getELContext(), "cities", String.class);
ValueExpression pickListItemLabel = ef.createValueExpression(
context.getELContext(), "#{cities}", String.class);
ValueExpression pickListItemValue = ef.createValueExpression(
context.getELContext(), "#{cities}", String.class);
pickList.setId("pickList");
pickList.setValueExpression("value", pickListValue);
pickList.setValueExpression("var", pickListVar);
pickList.setValueExpression("itemLabel", pickListItemLabel);
pickList.setValueExpression("itemValue", pickListItemValue);
form.getChildren().add(pickList);
Note: ruleManagedBean.cities refers to the DualListModel, populated as
List<String> citiesSource = new ArrayList<String>();
List<String> citiesTarget = new ArrayList<String>();
citiesSource.add("San Francisco");
citiesSource.add("London");
citiesSource.add("Paris");
citiesSource.add("Istanbul");
citiesSource.add("Berlin");
citiesSource.add("Barcelona");
citiesSource.add("Rome");
cities.setSource(citiesSource);
cities.setTarget(citiesTarget);
Make sure that you have declared this DualListModel globally as below and has getter setter methods
private DualListModel<String> cities = new DualListModel<String>();
Instead of PickList pickList = new PickList(); try this:
Application application = FacesContext.getCurrentInstance().getApplication();
PickList pickList = (PickList) application.createComponent(PickList.COMPONENT_TYPE);
Change your pickList.setValue(cities); for this:
Note: cities must be a field on your bean with its own getter.
ExpressionFactory ef = application.getExpressionFactory();
ELContext elc = FacesContext.getCurrentInstance().getELContext();
ValueExpression value = ef.createValueExpression(elc, "#{bean.cities}", DualListModel.class);
pickList.setValueExpression("value", value);
I have composite component:
<my:component value="#{bean.property1.property2}/>
From composite component I need to get class of bean.property1 to read its annotations.
I do it by the following code:
ValueExpression valueExpression = expressionFactory.createValueExpression(FacesContext.getCurrentInstance().getELContext(),
"#{bean.property1}", Object.class);
Object bean = valueExpression.getValue(FacesContext.getCurrentInstance().getELContext());
Class<?> beanClass = bean.getClass();
This works well but if I use my:component from a facelet and pass bean as a parameter via ui:param this does not work because bean can't be resolved.
Probably I should use FaceletContext as ELContext instead of FacesContext.getCurrentInstance().getELContext():
FaceletContext faceletElContext = (FaceletContext) FacesContext.getCurrentInstance().getAttributes()
.get("javax.faces.FACELET_CONTEXT");
But this doesn't work on RENDER_RESPONSE phase (from encodeBegin method). It returns last used ELContext instead of actual context (I am not surprised :) ).
The goal is to get class of #{bean.property1} from my:component. How can I do it?
This is easy with RichFaces:
ValueExpressionAnalayser analyser = new ValueExpressionAnalayserImpl();
ValueDescriptor valueDescriptor = analyser.getPropertyDescriptor(context, valueExpression);
Class<?> beanClass = valueDescriptor.getBeanType();
This is ok for me.
Also there is ValueExpressionAnalayzer in javax.faces.validator package but it is package private and can't be used.
You could pass the bean as a parameter to the component.
1) Declare the attribute in the component interface file(if you are using composite component):
<cc:interface componentType="myComponentClass">
<cc:attribute name="myBean" preferred="true"/>
..others attributes
<cc:interface>
2) Implement the respective getter and setter for "myBean" attribute in the component class(myComponentClass)
protected enum PropertyKeys {
myBean;
String toString;
PropertyKeys(String toString) {
this.toString = toString;
}
PropertyKeys() {}
#Override
public String toString() {
return ((this.toString != null) ? this.toString : super.toString());
}
}
public YourBeanClass getMyBean() {
return (YourBeanClass) getStateHelper().eval(PropertyKeys.myBean, null);
}
public void setMyBean(YourBeanClass myBean) {
getStateHelper().put(PropertyKeys.myBean, myBean);
}
3) Set the attribute on you jsf page:
<my:component myBean="#{bean}"/>
4) In the component's render class cast the UIComponent to myComponentClass.
#Override
public void encodeBegin(FacesContext pContext, UIComponent pComponent)
throws IOException {
myComponentClass myComponent = (myComponentClass) pComponent;
myComponent.getYourAttribute();
}
I'm probably too blind and too new to OmniFaces, and could not find a basic method in the API to retrieve a backing bean instance. Where can I find such a method if there is one? Like this one:
public static Object getBackingBean(String name) {
FacesContext context = FacesContext.getCurrentInstance();
Application app = context.getApplication();
ValueExpression expression = app.getExpressionFactory()
.createValueExpression(context.getELContext(), String.format("#{%s}", name), Object.class);
return expression.getValue(context.getELContext());
}
Or a more dynamic version with generics:
public static <T> T getBackingBean(String name) {
FacesContext context = FacesContext.getCurrentInstance();
Application app = context.getApplication();
ValueExpression expression = app.getExpressionFactory()
.createValueExpression(context.getELContext(), String.format("#{%s}", name), Object.class);
return (T) expression.getValue(context.getELContext());
}
We have a method that's nearly like that, but it can evaluate (and get) any kind of expression not just a simplified root expression.
It's Faces.evaluateExpressionGet.
You use it as follows:
MyBean myBean = Faces.evaluateExpressionGet("#{myBean}");
With MyBean being e.g. defined as follows:
#ViewScoped
#ManagedBean
public class MyBean {
// ...
}
Following java code allows to access any object or variable from faces context:
ELContext elCtx = facesContext.getELContext();
ExpressionFactory exprFac = facesContext.getApplication().getExpressionFactory();
MyProperty myProperty = (MyProperty) exprFac.createValueExpression(elCtx, "#{somebean.someattr.someproperty}", MyProperty.class).getValue(elCtx);
I use the code from within my custom converter to read additional converting parameters from context.
The code works correctly if #{somebean} is defined as normal backing bean within JSF context.
Facelets allow to create 'shortcut' to JSF expressions. Example:
<ui:param name="shortcut" value="#{somebean.someattr.someproperty}" />
<div>#{somebean.someattr.someproperty} equals #{shortcut}</div>
In this case both #{somebean.someattr.someproperty} and #{shortcut} have the same value.
However these 'shortcut' names are not accessible using java code above. For example:
MyProperty myProperty1 = (MyProperty) exprFac.createValueExpression(elCtx, "#{somebean.someattr.someproperty}", MyProperty.class).getValue(elCtx);
// myProperty1 has expected value
MyProperty myProperty2 = (MyProperty) exprFac.createValueExpression(elCtx, "#{shortcut}", MyProperty.class).getValue(elCtx);
// myProperty2 is null
Is there a way to access a facelets context and to read 'shortcut' parameter values, defined on the current JSF page?
I had the same problem and have chosen the following approach:
/**
* Führt eine Expression im aktuellen Faces EL Context
* UND im Facelets El Context aus.
*
* #param facesContext
* #param expression
* #return object
*/
private static Object executeExpressionInUIContext (final FacesContext facesContext, final String expression) {
final ELContext elContext = facesContext.getELContext();
final Application application = facesContext.getApplication();
Object result = executeExpressionInElContext(application, elContext, expression);
if (null == result) {
FaceletContext faceletElContext = (FaceletContext) FacesContext.getCurrentInstance().getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
result = executeExpressionInElContext(application, faceletElContext, expression);
}
return result;
}
private static Object executeExpressionInElContext (Application application, ELContext elContext, String expression) {
ExpressionFactory expressionFactory = application.getExpressionFactory();
ValueExpression exp = expressionFactory.createValueExpression(elContext, expression, Object.class);
return exp.getValue(elContext);
}
"ui:param" is part of the Facelet view handling technology. Facelets extends JSF.
Both technologies use their own Context when storing variables.
Beside the Faces El Context there is a Facelet El Context (FaceletContext).
The stated method evaluates expressions in both contexts. Be aware that this will not work if two values are stored under the same name in each context.
It seems that facelet shortcuts do not exist in the context, where I try to access them.
I have made following workaround: On JSF page where my input element is placed, I have added a <f:param> element as child of the input with my converter.
<h:inputText id="myid" value="#{action.myinput}">
<f:converter converterId="myConverter" />
<f:param name="converterParameters" shortcut="#{somebean.someattr.someproperty}"/>
</h:inputText>
Then in converter I'm able to find UIParam element as one of the input children and read my shortcuts from it.
public Object getAsObject(FacesContext context, UIComponent component, String value) {
MyProperty myProperty = null;
try {
for (final UIComponent child : component.getChildren()) {
if ("converterParameters".equals(child.getAttributes().get("name"))) {
final ELContext elCtx = context.getELContext();
myProperty = (MyProperty) child.getValueExpression("shortcut").getValue(elCtx);
break;
}
}
if (myProperty == null) {
throw new NullPointerException("My property is undefined.");
}
} catch (Exception e) {
LOG.error("Cannot convert " + value + ". Use <f:param name=\"converterParameters\" "
+ "shortcut=\"#{here.comes.shortcut}\"/> for your input element. ", e);
throw new ConverterException("Cannot initialize converter.", e);
}
//...
}
The mapping of ui:param is not stored in context, it's in the VariableMapper of each individual ValueExpression.
So if you need to create ValueExpression programmatically, relying on another ValueExpression's varMapper, you can do something like this:
VariableMapper varMapper = new DefaultVariableMapper();
varMapper.setVariable(mappingName, component.getValueExpression(mappedAttributeName));
return new ValueExpressionImpl(expression, null, null, varMapper, expectedType);
I know about startElement, endElement, and writeAttribute methods on ResponseWriter. My problem is that I want to for example output a h:commandLink by declaring it like HtmlCommandLink link = new HtmlCommandLink(); .
How can I output other UIComponents like this in my own component? I might want to use some RichFaces ajax stuff in my components aswell so hoping I can avoid making it all by scratch.
Edit: What I'm trying to do is create my own tag library with the following tag <myTags:commentTree>. Every comment have a reply button, when the reply button is clicked I render the reply form beneath the comment. Once that is rendered, I would like to output for example the richfaces <a4j:commandButton> component. This have to be done inside my own java tag file which Ive called for CommentsTreeUI.java.
Normally I output all my elements that display the forms and buttons with writer.startElement("input", myComponent); writer.writeAttribute("type", "button", null); but if I could instead do for example startElement("a4j:commandbutton", myComponent) that would help my ALOT since it has all the built in ajax features etc.
Any clues?
This problem was solved by adding new components by using
HtmlCommandButton button = new HtmlCommandButton();
button.encodeAll(context);
You can do something like this:
HtmlCommandLink link = new HtmlCommandLink();
getChildren().add(link);
It does depend on what you want to do with the child components though i.e. if you want them surrounded with custom HTML (in an HTML list, for example) you will need something a bit more complex.
One approach to making composite controls is to use the binding attribute to associate the tag with your own code:
<f:view>
<h:form>
<h:panelGroup binding="#{compositeControlBean.panelGrid}" />
</h:form>
</f:view>
The bean configuration in faces-config.xml:
<managed-bean>
<managed-bean-name>compositeControlBean</managed-bean-name>
<managed-bean-class>
composite.CompositeControlBean
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
The bean code:
/**
* Configure this bean in request scope as "compositeControlBean".
*/
public class CompositeControlBean {
private transient UIComponent panelGrid;
public UIComponent getPanelGrid() {
if (panelGrid == null) {
panelGrid = createCompositePanel();
}
return panelGrid;
}
public void setPanelGrid(UIComponent panelGrid) {
this.panelGrid = panelGrid;
}
private UIComponent createCompositePanel() {
initContextMemebers();
UIComponent commandLink = createCommandLink();
String id = view.createUniqueId();
UIComponent panelGrid = application
.createComponent("javax.faces.HtmlPanelGroup");
panelGrid.setId(id);
panelGrid.setRendererType("javax.faces.Group");
panelGrid.getChildren().add(commandLink);
return panelGrid;
}
private UIComponent createCommandLink() {
// create control
String id = view.createUniqueId();
UIComponent commandLink = application
.createComponent("javax.faces.HtmlCommandLink");
commandLink.setId(id);
commandLink.setRendererType("javax.faces.Link");
// set attributes (bind to printHello method)
Map<String, Object> attributes = commandLink
.getAttributes();
MethodExpression action = expressionFactory
.createMethodExpression(elContext,
"#{compositeControlBean.printHello}",
String.class, new Class<?>[0]);
attributes.put("value", "print hello");
attributes.put("actionExpression", action);
return commandLink;
}
private transient FacesContext context;
private transient Application application;
private transient ELContext elContext;
private transient ExpressionFactory expressionFactory;
private transient UIViewRoot view;
private void initContextMemebers() {
context = FacesContext.getCurrentInstance();
application = context.getApplication();
elContext = context.getELContext();
expressionFactory = application.getExpressionFactory();
view = context.getViewRoot();
}
public String printHello() {
System.out.println("Hello");
return null;
}
}