Seam's improvement
alpha88 Mar 10, 2009 6:46 AMHi,
I have some improvements about Seam. See the following:
// import statements
@Stateless
@Name("messageService")
public class MessageServiceImpl implements MessageService {
@PersistenceContext
private EntityManager em;
// equals signal omitted
@DataModel(scope=ScopeType.PAGE)
private List<Message> messageList;
@DataModelSelection
@Out(required=false)
private Message selectedMessage;
public void getMessage(Integer messageId) {
selectedMessage = em.find(Message.class, messageId);
}
public void getMessage() {
// selectedMessage has been retrieved through @DataModelSelection, so no action is needed
return;
}
@Factory("messageList")
public void getAllMessage() {
messageList = em.createQuery("from Message m order by m.info asc").getResultList();
}
}
If i have requested a getAllMessage.seam as follow:
<h:form>
// throws @Factory("messageList") and fills out @DataModel messageList in MessageServiceImpl
<h:dataTable value="#{messageList}" var="_message">
<h:column>
#{_message.info}
</h:column>
<h:column>
<s:link value="#{_message.info}" action="#{messageService.getMessage}"/>
</h:column>
<h:column>
<h:commandLink value="#{_message.info}" action="#{messageService.getMessage(_message.id)}"/>
</h:column>
</h:dataTable>
</h:form>
See the code above: why do i use "s:link" when the action is getMessage and "h:commandLink" when the action is getMessage(_message.id) ?
Answer is found in MessageServiceImpl:
@DataModelSelection(scope=ScopeType.PAGE)
private List<Message> messageList;
ScopeType.PAGE says: remember my state at next postback OR save my state in the JSF UI tree. Ok. It happens "s:link" does not work if state is stored at JST UI tree. "h:commandLink" does. By the way, "h:commandLink" needs to be inserted inside a "h:form"; "s:link" does not; So, the postback needs remember which _message has been selected when you call "action=#{messageService.getMessage(_message.id)}". "h:commandLink" fulfill this requirement. If you use a "s:link" then you must call "action=#{messageService.getMessage}" with no arguments and use a @DataModelSelection to gain access to message selected in @DataModel.
Ok, let's go. So, what's the problem ?
first: if you use a getMessage method, what comes in mind ? here the answer: which message ? So we need a parameter, often a id in order to retrieve the requested message. Because "s:link" does not allows to pass parameters whether we use a @DataModel in PAGE scope, we have to adapt our getMessage with no arguments in order to use "s:link" because seam does not provide this funcionality.
What i suggest ? Something like this:
A "action" tag nested a "s:link" that support simple Java types - primitives and Wrappers
<s:link>
<action method="getMessage">
<param>#{_message.id}</param>
</action>
</s:link>
It will throws getMessage(Integer id)
It could be use a JSF converter like this one
<s:link>
<action method="#{messageService.getMessage}">
<param converterType="javax.faces.convert.IntegerConverter">#{_message.id}</param>
</action>
</s:link>
Or allows multiple simple Java Types parameters like this one whether we use a composite id
<s:link>
<action method="#{messageService.getMessage}">
<param converterType="javax.faces.convert.IntegerConverter">#{_message.id1}</param>
<param converterType="javax.faces.convert.IntegerConverter">#{_message.id2}</param>
<param converterType="javax.faces.convert.IntegerConverter">#{_message.id3}</param>
</action>
</s:link>
It will throw getMessage(Integer id1, Integer id2, Integer id3)
Cool, don't. This way we do not have to put @DataModel in PAGE scope. We could simply put it in a EVENT scope, which is lighter.
Now, let's consider the following scenario: we have removed the @DataModel and @DataModelSelection. Again, there is another approach to @Factory method as follows
// ATT: @DataModel can not be marked in method
@Factory("messageList")
public List<Message> getMessageList() {
return em.createQuery("from Message m order by m.info asc").getResultList();
}
Notice that getMessageList returns a List<Message> (opposed to void in previous example). This way, we cannot use <h:dataTable because @Factory method has been not encapsulated in a @DataModel as follows
// It does not work. Factory method needs to be encapsuled in a @DataModel.
<h:dataTable value="#{messageList}" var="_message">
What's the solution ?
// ui facelets
<ui:repeat var="_message" value="#{messageList}">
#{message.info}
</ui:repeat>
With param tag, as propposed, we could be use as follows, even whether our MessageServiceImpl does not contain a @DataModel and @DataModelSelection:
<ui:repeat var="_message" value="#{messageList}">
<p>
<s:link value="#{message.info}">
<action method="#{messageService.getMessage}">
<param>#{_message.id}</param>
</action>
</s:link>
</p>
</ui:repeat>
ATT: as we have left @DataModel behind, we can not use something like this
<s:link action="#{messageService.getMessage}" because neither @DataTable nor @DataModelSelection lives in MessageServiceImpl anymore.
Regards,
Arthur Ronald F D Garcia - Java programmer
(Natal - Brazil)
I have some improvements about Seam. See the following:
// import statements
@Stateless
@Name("messageService")
public class MessageServiceImpl implements MessageService {
@PersistenceContext
private EntityManager em;
// equals signal omitted
@DataModel(scope=ScopeType.PAGE)
private List<Message> messageList;
@DataModelSelection
@Out(required=false)
private Message selectedMessage;
public void getMessage(Integer messageId) {
selectedMessage = em.find(Message.class, messageId);
}
public void getMessage() {
// selectedMessage has been retrieved through @DataModelSelection, so no action is needed
return;
}
@Factory("messageList")
public void getAllMessage() {
messageList = em.createQuery("from Message m order by m.info asc").getResultList();
}
}
If i have requested a getAllMessage.seam as follow:
<h:form>
// throws @Factory("messageList") and fills out @DataModel messageList in MessageServiceImpl
<h:dataTable value="#{messageList}" var="_message">
<h:column>
#{_message.info}
</h:column>
<h:column>
<s:link value="#{_message.info}" action="#{messageService.getMessage}"/>
</h:column>
<h:column>
<h:commandLink value="#{_message.info}" action="#{messageService.getMessage(_message.id)}"/>
</h:column>
</h:dataTable>
</h:form>
See the code above: why do i use "s:link" when the action is getMessage and "h:commandLink" when the action is getMessage(_message.id) ?
Answer is found in MessageServiceImpl:
@DataModelSelection(scope=ScopeType.PAGE)
private List<Message> messageList;
ScopeType.PAGE says: remember my state at next postback OR save my state in the JSF UI tree. Ok. It happens "s:link" does not work if state is stored at JST UI tree. "h:commandLink" does. By the way, "h:commandLink" needs to be inserted inside a "h:form"; "s:link" does not; So, the postback needs remember which _message has been selected when you call "action=#{messageService.getMessage(_message.id)}". "h:commandLink" fulfill this requirement. If you use a "s:link" then you must call "action=#{messageService.getMessage}" with no arguments and use a @DataModelSelection to gain access to message selected in @DataModel.
Ok, let's go. So, what's the problem ?
first: if you use a getMessage method, what comes in mind ? here the answer: which message ? So we need a parameter, often a id in order to retrieve the requested message. Because "s:link" does not allows to pass parameters whether we use a @DataModel in PAGE scope, we have to adapt our getMessage with no arguments in order to use "s:link" because seam does not provide this funcionality.
What i suggest ? Something like this:
A "action" tag nested a "s:link" that support simple Java types - primitives and Wrappers
<s:link>
<action method="getMessage">
<param>#{_message.id}</param>
</action>
</s:link>
It will throws getMessage(Integer id)
It could be use a JSF converter like this one
<s:link>
<action method="#{messageService.getMessage}">
<param converterType="javax.faces.convert.IntegerConverter">#{_message.id}</param>
</action>
</s:link>
Or allows multiple simple Java Types parameters like this one whether we use a composite id
<s:link>
<action method="#{messageService.getMessage}">
<param converterType="javax.faces.convert.IntegerConverter">#{_message.id1}</param>
<param converterType="javax.faces.convert.IntegerConverter">#{_message.id2}</param>
<param converterType="javax.faces.convert.IntegerConverter">#{_message.id3}</param>
</action>
</s:link>
It will throw getMessage(Integer id1, Integer id2, Integer id3)
Cool, don't. This way we do not have to put @DataModel in PAGE scope. We could simply put it in a EVENT scope, which is lighter.
Now, let's consider the following scenario: we have removed the @DataModel and @DataModelSelection. Again, there is another approach to @Factory method as follows
// ATT: @DataModel can not be marked in method
@Factory("messageList")
public List<Message> getMessageList() {
return em.createQuery("from Message m order by m.info asc").getResultList();
}
Notice that getMessageList returns a List<Message> (opposed to void in previous example). This way, we cannot use <h:dataTable because @Factory method has been not encapsulated in a @DataModel as follows
// It does not work. Factory method needs to be encapsuled in a @DataModel.
<h:dataTable value="#{messageList}" var="_message">
What's the solution ?
// ui facelets
<ui:repeat var="_message" value="#{messageList}">
#{message.info}
</ui:repeat>
With param tag, as propposed, we could be use as follows, even whether our MessageServiceImpl does not contain a @DataModel and @DataModelSelection:
<ui:repeat var="_message" value="#{messageList}">
<p>
<s:link value="#{message.info}">
<action method="#{messageService.getMessage}">
<param>#{_message.id}</param>
</action>
</s:link>
</p>
</ui:repeat>
ATT: as we have left @DataModel behind, we can not use something like this
<s:link action="#{messageService.getMessage}" because neither @DataTable nor @DataModelSelection lives in MessageServiceImpl anymore.
Regards,
Arthur Ronald F D Garcia - Java programmer
(Natal - Brazil)