JSF Design Question

2006-02-09 Thread Adam Brod

Hi-

I have run into a few problems moving
from the struts way of thinking to JSF. I'm hoping that the many
smart people on this list have made that jump. I'm stuck trying to
design the most elegant way to implement a piece of functionality I'll
call Update Document. I would appreciate it if anybody
can help. I'm sure many people have run into a similar design question.

Here are the relevant objects:
-Document
(this is the domain object being updated)
-DocumentRepository
(this is like a DAO object - it retrieves and persists the Document)
-DocumentController
(this is a POJO JSF-managed bean with request scope)
-UpdateDocument.jsp
(this is the JSF view that displays
the Update Document form)

Here are my constraints:
- My DocumentController (a managed bean)
should not use any Faces/Http objects. That is, no Request, no FacesContext,
etc. All request parameters should automatically map to objects
- I don't want to store anything in
the session
- I want to write the least code possible
(while still being manageable)

Here's what I would like to have happen
(but can't figure out how to do it):
1) On the Update Document page, the
user update the document title and clicks save 
2) JSF instantiates a new documentController
(request scope)
3) JSF passes the documentID
to the controller
4) The documentController
loads the Document from the DocumentRepository
5) JSF calls documentController.getDocument()
and maps the values from the Update Document form to the Document domain
object 
(e.g.,
h:inputText
id=title
value=#{documentController.document.title}/)

What I like about my approach is that
JSF just sets the values directly on my domain object. I don't want
to have to create a dummy FormBean that JSF uses to set the parameters
because then I would be forced to do a second round of copying from the
FormBean to the domain object. The domain object is actually just
an interface that only the repository can instantiate.

My problem is that I can't ever get
JSF to execute step 3 before step 5 gets called. I've tried using
the updateActionListener, but that gets invoked after the values are set.
I've tried using a hidden parameter to pass the documentID to the
documentController, but that didn't seem to work either. Below I've
included a simplified DocumentController and the Update Document form.

Does anybody have any advice??

Thanks, 
Adam

public class DocumentController
{
  // if the
documentID is set, this will load the Document from the repository
  public Document
getDocument();
  public void
setDocumentID(long id);

  // action
method to save Document to the repositor
  public String
save();
}

This is what my UpdateDocument form
looks like:
h:form
id=updateDocument
  h:panelGrid
columns=2


  h:column

   h:outputLabel
value=#{msgs.title}
for=title
/
/h:column
h:column
   

 h:inputText
id=title
value=#{documentController.document.title}
required=true

  f:validateLength
minimum=3
maximum=25
/

 /h:inputText

 h:message
for=title
styleClass=errorMessage
/

   /h:column

   h:column

 h:outputLabel
value=#{msgs.email}
for=email
/
/h:column
h:column
   

 h:inputText
id=email
value=#{documentController.document.email}
required=true
/

 h:message
for=email
styleClass=errorMessage
/
 
/h:column

   
  /h:panelGrid
  h:commandButton
id=submit
action=#{documentController.save}
value=#{msgs.enter}
/
  h:commandButton
id=cancel
action=cancel
value=#{msgs.cancel}
/
/h:form



Re: JSF Design Question

2006-02-09 Thread Werner Punz
There is no need for such a complicated approach what you try to achieve 
is a state saving and scoping over the database.
MyFaces tomahawk has a x:saveState construct which first restores the 
entire object and then sets the altered values.
No id nothing is needed once you have the data (which you obviously had 
before otherwise your approach would not work.


As for the calling order, I had a similar problem recently, and I solved 
it with the fact that the calling order basically is resembled by the 
element order in the tree.
Which means if you have your document id on to with a hidden field the 
setters and getters are called before the rest so you can use the 
setters and getters of your controller bean for preinitialization.
That is very dirty however (I am not sure if this is a behavior working 
over all implementations it works however on MyFaces)


The cleaner way would be to use a phase listener which gets the data 
from the request and does some preinitialization or use something like 
shale which sets clear interception points in the controller itself)


But as for your problem you probably can settle down to x:saveState 
instead of having such a complicated construct where you try to achieve 
a scoping via db surrogates and db state saving.


Werner



Adam Brod schrieb:


Hi-

I have run into a few problems moving from the struts way of thinking to 
JSF.  I'm hoping that the many smart people on this list have made that 
jump.  I'm stuck trying to design the most elegant way to implement a 
piece of functionality I'll call Update Document.  I would appreciate 
it if anybody can help.  I'm sure many people have run into a similar 
design question.


Here are the relevant objects:
-Document (this is the domain object being updated)
-DocumentRepository (this is like a DAO object - it retrieves and 
persists the Document)

-DocumentController (this is a POJO JSF-managed bean with /request/ scope)
-UpdateDocument.jsp (this is the JSF view that displays the Update 
Document form)


Here are my constraints:
- My DocumentController (a managed bean) should not use any Faces/Http 
objects.  That is, no Request, no FacesContext, etc.  All request 
parameters should automatically map to objects

- I don't want to store anything in the session
- I want to write the least code possible (while still being manageable)

Here's what I would like to have happen (but can't figure out how to do 
it):
1) On the Update Document page, the user update the document title and 
clicks save

2) JSF instantiates a new documentController (request scope)
3) JSF passes the documentID to the controller
4) The documentController loads the Document from the DocumentRepository
5) JSF calls documentController.getDocument() and maps the values from 
the Update Document form to the Document domain object
(e.g., h:inputText id=title 
value=#{documentController.document.title}/)


What I like about my approach is that JSF just sets the values directly 
on my domain object.  I don't want to have to create a dummy FormBean 
that JSF uses to set the parameters because then I would be forced to do 
a second round of copying from the FormBean to the domain object.  The 
domain object is actually just an interface that only the repository can 
instantiate.


My problem is that I can't ever get JSF to execute step 3 before step 5 
gets called.  I've tried using the updateActionListener, but that gets 
invoked after the values are set.  I've tried using a hidden parameter 
to pass the documentID to the documentController, but that didn't seem 
to work either.  Below I've included a simplified DocumentController and 
the Update Document form.


Does anybody have any advice??

Thanks,
Adam

public class DocumentController {
// if the documentID is set, this will load the Document from the 
repository

public Document getDocument();
public void setDocumentID(long id);

// action method to save Document to the repositor
public String save();
}

This is what my UpdateDocument form looks like:
h:form id=updateDocument
h:panelGrid columns=2 
  h:column
h:outputLabel value=#{msgs.title} for=title /
/h:column
h:column
h:inputText id=title 
value=#{documentController.document.title} required=true

  f:validateLength minimum=3 maximum=25 /
/h:inputText
h:message for=title styleClass=errorMessage /
/h:column
h:column
h:outputLabel value=#{msgs.email} for=email /
/h:column
h:column
h:inputText id=email 
value=#{documentController.document.email} required=true /
h:message for=email styleClass=errorMessage /
/h:column
   
/h:panelGrid
h:commandButton id=submit action=#{documentController.save} 
 value=#{msgs.enter} /

h:commandButton id=cancel