Wednesday, October 14, 2009

Dynamic Navigation in JSF

The last two post was about the use of dynamic JSF components and properties, but the other problem I faced is referred with the dynamic navigation,

"How can I extend or write a more dynamic navigation in JSF? for example to create dynamic parameters or calls to a non jsf pages with GET parameters?"

Ok, here's how I solved this. In the method of the backing bean that will be called when I the button is pressed I added this code:


FacesContext currentInstance = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) currentInstance.getExternalContext().getResponse();
HttpServletRequest request = (HttpServletRequest) currentInstance.getExternalContext().getRequest();
String url = request.getContextPath();
if (a == 1) {
url += "testpage.jsp?arg1=1&arg2=2";
} else {
url += "testpage2.jsp?arg1=1&arg2=3";
}
response.sendRedirect(url);
currentInstance.responseComplete();


As you can see here the solution it's very simple and works very well.

Tuesday, October 13, 2009

How Can I bind the jsf properties dynamically?

In my previous post I explained how you would be able to create the controls dynamically, now it's time to bring this baby alive!

First, let me explain how you would create Value Expressions (that will be used by faces to get the values) and bind them to your recently created HtmlInputText,


FacesContext facesInstance = FacesContext.getCurrentInstance();
Application application = facesInstance.getApplication();
ExpressionFactory expressionFactory = application.getExpressionFactory();

String expression = "#{myMode.myProperty}";
ValueExpression valExpression = expressionFactory.createValueExpression(facesInstance.getELContext(), expression, String.class);

HtmlInputText text = new HtmlInputText();
input.setValueExpression("value", valExpression);


Easy, isn't it? the first 3 lines will get the context required to create the ValueExpression, the line "String expression = ..." just creates the string expression, the one that you usually put in the value of the h:inputText in your jsf page. The next two lines create the expression and assign it to the "value" property of the html input text. You could use this in the required or rendered fields (any property that you could use as EL in your page).

This solution works well if you have your properties hardcoded in your bean, but how will this work with a really dynamic solution? (if you already know them, why will you need to create all the dynamic controls in the first place?)

Ok, here the Expression Language will do the trick, the Expression Language allow you to put Maps as properties in your code, so you could create your own Map to your dynamic properties and that's it! here's a sample of the Map (I will not put all the methods you should figure out how to solve the others, but it's very simple)


public class MyPropertyMap implements java.util.Map {
private java.util.HashMap internalMap;
public EntityMap() {
internalMap = new java.util.Map();
internalMap.put("PropertyA", 1);
internalMap.put("PropertyB", "A text property");
internalMap.put("PropertyC", True);
}

public Object get(Object key) {
return internalMap.get(key);
}

public void put(Object key, Object value) {
internalMap.put(key, value);
}

... (all the other methods required by the Map interface)
}

public MyModel {
private MyPropertyMap map = new MyPropertyMap();

public Object getMyPropertyMap() {
return map;
}

public void setMyPropertyMap(MyPropertyMap map) {
this.map = map;
}
}


finally your dynamic control creation should look like this:


FacesContext facesInstance = FacesContext.getCurrentInstance();
Application application = facesInstance.getApplication();
ExpressionFactory expressionFactory = application.getExpressionFactory();

String expression = "#{myMode.myPropertyMap['PropertyA']}";
ValueExpression valExpression = expressionFactory.createValueExpression(facesInstance.getELContext(), expression, Integer.class);

HtmlInputText text = new HtmlInputText();
input.setValueExpression("value", valExpression);
myPanel.getChildren().add(input);

String expression2 = "#{myMode.myPropertyMap['PropertyB']}";
ValueExpression valExpression2 = expressionFactory.createValueExpression(facesInstance.getELContext(), expression2, String.class);

HtmlInputText text2 = new HtmlInputText();
input2.setValueExpression("value", valExpression2);
myPanel.getChildren().add(input2);

String expression3 = "#{myMode.myPropertyMap['PropertyC']}";
ValueExpression valExpression3 = expressionFactory.createValueExpression(facesInstance.getELContext(), expression3, Boolean.class);

HtmlSelectBooleanCheckbox chk = new HtmlSelectBooleanCheckbox();
chk.setValueExpression("value", valExpression3);
myPanel.getChildren().add(chk);


and that's it... you have a full Dynamic page! (I hope you noticed how the 3rd parameter of the createValueExpression changed for each control, Integer, String and Boolean).

Ok, with this you will have a full dynamic solution, the next part will show you how you could create dynamic navigation using dynamic parameters.

Dynamic JSF

Recently I came across with a "problem", how can I create some JSF pages more dynamic than usual?, for example adding new controls on the fly using metadata from somewhere else. I decided to write this article because it's a little hard to find information about this subject, and when you find it you could not spot all the solution and all the problems that will face in the real life :D.

This is not a solution I invented and I can barely remember all the links I read in order to understand the whole picture, later I will try to look at my browser history in order to compile them and give the credit they deserved.

Ok, here is the description of the problem I want to solve:

How can I create dynamic controls for a page that needs to be solved on runtime? the application I'm working on is a BPM engine (BizAgi) that constructs its pages based on metadata information stored in a DB, so it's not possible to write the controls from the beginning.

The solution is fairly simple and has 3 parts:

1. Dynamic creation of the controls
2. Dynamic mapping of the properties
3. Dynamic redirection to a JSP using parameters.

Dynamic control creation

This is the easy part and there's a lot of documentation in internet about this subject, and the steps are easy:

1. Create a Panel in your JSF page
2. Bind the panel to a property in your backing bean
3. Add the controls in your backing bean

so, here's how it's done:

create a bean class (model class) with a property of type javax.faces.component.UIPanel like this:


public class MyModel {
private javax.faces.component.UIPanel myPanel;

public javax.faces.component.UIPanel getMyPanel() {
return myPanel;
}

public void setMyPanel(javax.faces.component.UIPanel myPanel) {
this.myPanel = myPanel;
}
}


Next just bind it in your jsf page like this:


<f:view>
<h:panelGrid binding="#{myModel.myPanel}" />
</f:view>


we're close to finish, the next step is where the fun begins, just add the code to create dynamically the controls, like this:



public void setMyPanel(javax.faces.component.UIPanel myPanel) {
this.myPanel = myPanel;
if (myPanel.getChildCount() <= 1) {
HtmlInputText input = new HtmlInputText();
input.setValue("my dynamic text");
myPanel.getChildren().add(input);
}
}


At this point, I'm sure you noticed the "getChildCount() <= 1"... why is that? just because everytime the postback is called the faces framework will set the panel with the last controls added to it, if you don't control that you will get a panel with multiple text everytime you hit the "save" button. I know, you can do this in several ways, but this is an example!

Ok, this is the point where all the "blog" gurus stop and you get yourself in the middle of nowhere!, you must ask questions like... how will I bind that to dynamic properties instead of static texts? or, how would I manage the state... etc.

Because the blog is getting longer than I thought I will split this in some parts...

How can I bind the jsf properties dynamically?
How can I send dynamic parameters to other JSPs? or JSFs?

As usual please forgive my pour english, I'm hoping to improve it with every post xD