(moved to new topic heading, but keeping seem referenceID for those threading)..

Michael Jouravlev wrote the following on 7/7/2005 1:40 PM:

Ok, Rick, lets pull out and compare :-) But before I go into
presenting my approach, could you tell me, what happens in your app
when a user clicks Refresh? Or Back?

Sure. No problem. It depends on the application. If it's a requirement that data does not get submitted twice I simply use the token approach that is very easy to implement. It doesn't take much at all to ensure duplicate entries do not get entered. If the user hits 'back' on their browser button most of the time that page info displayed is cached by the browser. If it's critical that the page always show fresh data (which usually it is not) then ther are ways to ensure that also (doing the pragma, no-cache stuff like you do in your DialogAction).

I will use my DialogAction, which subclasses DispatchAction, adding an
important feature to it: two-phase request processing aka
Redirect-after-Post. It helps to avoid resubmits, and makes the whole
process of input/change state/render view much easier.

Like I said if resubmits are in issue just use the Token approach. It's super easy and documented.

My DialogAction or its subclass CRUDAction (thanks for this great
example, I have action exactly for this use case) does not have any
default values. If you navigate to the action, it simply shows current
data for current state. So, as you see, my actionform has session
scope to be stateful.

My DispatchAction doesn't have default values either. I'm not sure what you are talking about here. If I want to keep state, I simply give my ActionForm session scope and or if I want list of users to have state I put it in session scope. What's new about giving an ActionForm session scope?


I don't care much about how VO/BO is mapped to actionform. Basically,
if you want values to be populated from request, and to be used in
JSP, either create setter/getters in the actionform, or set VO/BO as
nested property of the actionform.

Agreed. I often do the same (nest a VO/BO in my ActionForm, although this wouldn't be the initial approach I'd give someone just starting out).

I have generic CRUDAction, which has create(), duplicate(), edit(),
view() and delete() methods. And of course, getView(), which renders a
proper page.

Yes I see that.


DispatchAction and its subclasses accepts input as form submission
only (POST), because it detects input phase or render phase by request
type. I think, this is a reasonable limitation.

Not true. I pass stuff with a get all the time using a URL.

The corresponding create() method in CRUDAction from Struts Dialogs
looks like this:

    public ActionForward onCreate (ActionMapping mapping,
                                   ActionForm form,
                                   HttpServletRequest request,
HttpServletResponse response) throws Exception {

        // Action Form must implement ICRUDForm
        ICRUDForm crudForm = (ICRUDForm) form;

        // Create new business object (item), and get back errors if any
        ActionMessages messages = crudForm.crudCreate();

        // Successfully created new object and initialized action form
        if ((messages == null) || messages.isEmpty()) {
            // Need to handle this "forward" object in struts-config.xml
            return mapping.findForward(CRUDConstants.MAPPING_ON_CREATE_SUCCESS);

        // On error save messages into session. Struts 1.2.6+ has
        // a special method for it; this code saves errors directly
        // to session for compatibility with older Struts versions.
        } else {
            HttpSession session = request.getSession();
            session.setAttribute(Globals.ERROR_KEY, messages);
            // Handle this "forward" object in struts-config.xml
            return mapping.findForward(CRUDConstants.MAPPING_ON_CREATE_FAILURE);
        }
    }


Yes, I saw this in the code I download. I see what you are doing - basically have a generic Action that tells the form to update, delete, etc. Again, I think this is pretty 'cool' but I don't think it is that much of a time saver and it is definitely a totally different paradigm from all the Struts stuff out there. Not saying it's not a good concept, I'm just stating that is more difficult to grasp at first since it's not how Struts was desgined. You are free to mold it however you want and that's the beauty of open source, but I don't see your solution as that much more of a time saver or any easier to understand than the basic approaches.

ALSO I will amost GUARANTEE you that in reality you will almost always end up having to do other things when an update/edit/delete takes place... maybe you'l have requirements that you need to stuff something else into the request, maybe something else into the session - you'll say do this all in your the CRUDForm subclass, which yes you can do but to me it's not intutive. I think what IS intutive is getting a handle to HtttpServletRequest in a SERVLET. That's what people are used to using. Pushing this stuff off to a CRUDForm is definitely different.

// THE SET UP FOR EDIT

 Ok, this is the one you were asking about. Now you have to think about
how you would get here? Typically you'd get to an edit page by clicking
on a user record to say "Hey, I want to edit this guy's user
information"  So imagine a case where we have a list of users and
'userID" for each in the list, they click on the link and they'll come
to this method which will get our user and then forward them to a page
to do the editing. The method will look like...

public ActionForward setUpForEdit(ActionMapping mapping, ActionForm
form, HttpServletRequest request, HttpServletResponse response) throws
Exception {

  UserActionForm userForm = (UserActionForm)form;
  //userID is set in this form when the user clicked on the link
  //lets get our real business object of this user, based on ID..
  UserVO user = ourBackendDelegate.addUser( userForm.getUserID() );
  //copy the other way this time, from UserVO to our Form...
  PropertyUtils.copyProperties( userForm, user  );
  //finally we are going to reuse or form for add and edit, so
  //we will set up our dispatch/action parameter
  userForm.setDispatch("edit");
  return mapping.findForward("to_form");
}


I guess in the above method you meant to write  
  UserVO user = ourBackendDelegate.getUser( userForm.getUserID() );


Yes! Great catch..thanks:)


Anyway, my version does not differ from other handlers.
Data is loaded from the backend by crudForm.crudLoadForUpdate()
method. This method has to load existing data either directly into
action form or into nested BO/VO.

But it does differ as to where this stuff is being done.

//The get users - this will display our users on the page
public ActionForward getUsers(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {
  //lets say pass in companyID
  Integer companyID = Integer.valueOf( request.getParameter("companyID"));

  //get our list of users to display on page
  List users = ourBackendDelegate.getUsers( companyID );
  request.setAttribute("users", users );

  return mapping.findForward("to_display");
}


"Get users" is the action not directly related to CRUD, so it is not
implemented in CRUDAction, which is a standard action class and can be
used directly, without modification. "Get users" is implemented in a
separate action.

AHHH ok see, but now look... this why I think your solution is confusing.... Some DB stuff (gets, updates, etc) is being done in ActionForms (CRUDFOrms) and other times it's being done in standard Actions. I'm sorry I just find this confusing. Stuff being done in too many different places. Some done in ActionForms, some in Actions. Just provide a GetUserAction or getUsers dispatch method... an UpdateUserAction or updateUser dispatch method. It's consistent and easy for anyone to follow.



4) sample config...


<action path="/userMaintenance" type="com.foobar.UserAction"
            name="userActionForm"
            scope="request"
            validate="false"
            parameter="dispatch">
                <forward name="to_form" path="/userForm.jsp"/>
                <forward name="to_display" path="/displayUsers.jsp"/>
        </action>


Here is mine, it is not users, but abstract items with ID, stringvalue
and numvalue. You see a lot of mappings for different occasions, but
most of them simply reload the same action, and can be replaced with
standard "ON-RELOAD".

      <!-- CRUD action helper: item list -->
      <action path="/cruditemlist"
              type      =
"net.sf.dialogs.samples.crudaction.ItemListActionSample"
              name      = "dummyForm">
              <forward name = "showlist" path="/crudaction-list.jsp"/>
      </action>

      <!-- CRUD action -->
      <action path="/crudaction"
              type      = "net.sf.dialogs.actions.crud.CRUDAction"
              name      = "crudform"
              scope     = "session"
              validate  = "false"
              parameter = "DIALOG-EVENT">

          <!-- Where to hand control over after input phase (POST) -->

          <forward name="ON-CREATE-SUCCESS" path="/crudaction.do"
redirect="true"/>
          <forward name="ON-DUPLICATE-SUCCESS" path="/crudaction.do"
redirect="true"/>

          <forward name="ON-PREVIEW" path="/crudaction.do" redirect="true"/>
          <forward name="ON-EDIT" path="/crudaction.do" redirect="true"/>
          <forward name="ON-LOAD-FAILURE" path="/erroraction.do"
redirect="true"/>

          <forward name="ON-DELETE-SUCCESS" path="/cruditemlist.do"
redirect="true"/>
          <forward name="ON-DELETE-FAILURE" path="/erroraction.do"
redirect="true"/>

          <forward name="ON-CANCEL" path="/cruditemlist.do" redirect="true"/>
          <forward name="ON-CLOSE" path="/cruditemlist.do" redirect="true"/>

          <forward name="ON-STORE-SUCCESS" path="/cruditemlist.do"
redirect="true"/>

          <forward name="ON-STORE-FAILURE" path="/crudaction.do"
redirect="true"/>
          <forward name="ON-INVALID-DATA" path="/crudaction.do"
redirect="true"/>

          <!-- Which page to load on the output phase or on refresh (GET) -->

          <!-- Edit Mode -->
          <forward name="CRUD-UI-MODE-EDITABLE" path="/crudaction-edit.jsp"/>
          <!-- Preview Mode -->
          <forward name="CRUD-UI-MODE-READONLY" path="/crudaction-noedit.jsp"/>
          <!-- Illegal mode, item is not active -->
          <forward name="CRUD-UI-MODE-INACTIVE"
path="/crudaction-notactive.jsp"/>

      </action>
      <action path="/erroraction" forward="/crudaction-error.jsp"/>

5) Sample form

<html:form action="/userMaintenance">
    Name: <html:text property="name"/><br/>
    <html:hidden property="userID"/>
    <html:hidden property="dispatch"/>

    <html:submit value="${userForm.dispatch}"/>
    <!-- above button logic can be done many ways, just for
    simplicity i'm using the dispatch name-->
<html:form>


Here is mine, this one is for edit. There is another for view, which
does not have edit fields.

<html:form action="/crudaction.do">
  <table border="1" width="300" >
    <tr>
        <td>Item ID</td>
        <td><bean:write name="crudform" property="itemId"/></td>
    </tr>
    <tr>
        <td>String value</td>
        <td><html:text name="crudform" property="stringValue"/></td>
    </tr>
    <tr>
        <td>Num value</td>
        <td><html:text name="crudform" property="intValue"/></td>
    </tr>
    <tr>
      <td colspan="2">
        <INPUT type="submit" size="10" name="DIALOG-EVENT-CANCEL"
value="Cancel">&nbsp;
        <INPUT type="submit" size="10" name="DIALOG-EVENT-DELETE"
value="Delete">&nbsp;
        <INPUT type="submit" size="10" name="DIALOG-EVENT-SAVE"
value="Save Changes">
      </td>
    </tr>
   </table>
</html:form>

6) Sample page displaying users lets them edit and delete them

<c:forEach items='${users}' var='users'>
     <c:url var="editUrl" scope="page" value="/userMaintenance.do">
        <c:param name="dispatch" value="setUpForEdit"/>
        <c:param name="userID" value="${user.userID}"/>
    </c:url>
    <c:url var="removeUrl" scope="page" value="/userMaintenance.do">
        <c:param name="dispatch" value="delete"/>
        <c:param name="userID" value="${user.userID}"/>
    </c:url>
     ${user.name} <a href="${editUrl}">[ EDIT ]</a> <a
href="${removeUrl}">[ DELETE ]</a>
</c:forEach>


You mean the list? It does not directly relate to CRUD, does not it?

No, not specifically. But it does relate to a common event users want - a list of items for display. As I mentioned I'm curious where you would populate this list? According to your config file you'd be doing this in the Action class: ItemListActionSample which is what I'm doing also, but the difference is I'm handling all of my other opeartions in Actions also and not in ActionForms. You are doing some in ActionForms and some in Actions.

But here it is, for completeness:

<table border="1" width="100%">
  <tr>
    <th>ID</th>
    <th>Str Value</th>
    <th>Int Value</th>
    <th colspan="4">Operation</th>
  </tr>

  <logic:iterate id="item" name="crud-item-list"
type="net.sf.dialogs.samples.crudaction.business.BusinessObj">
  <html:form action="/crudaction.do">
    <tr>
      <td><bean:write name="item" property="itemId"/></td>
      <td><bean:write name="item" property="stringValue"/></td>
      <td><bean:write name="item" property="intValue"/></td>

      <td>
<html:submit property="DIALOG-EVENT-INIT-DUPLICATE" value="Duplicate Item"/>
      </td>

      <td>
<html:submit property="DIALOG-EVENT-INIT-VIEW" value="View Item"/>
      </td>

      <td>
<html:submit property="DIALOG-EVENT-INIT-UPDATE" value="Update Item"/>
      </td>

      <td>
<html:submit property="DIALOG-EVENT-DELETE" value="Delete Item"/>
      </td>
    </tr>
    <html:hidden name="item" property="itemId"/>
  </html:form>
  </logic:iterate>
</table>
<br/>
<html:form action="/crudaction.do">
<html:submit property="DIALOG-EVENT-INIT-CREATE" value="Create New Item"/>
</html:form>

Basically 90% of applications are doing something similar to the above.
Presenting data to users and often letting them click on things to edit.
Personally, this concept works well for me. There are several tweeks I
have to do for more complex things, but this is the 'meat' of most
applications.


This works for me too :-) Here is the live demo of above CRUD application:
http://www.superinterface.com/strutsdialog/cruditemlist.do

Looks nice. Where is the full source code for this application including the JSPs etc? I didn't see it in the dialog project that I downloaded?

Overall, at first glance, my conclusions so far haven't changed...

I think what you have so far is cool, and I think it's definitely more Object Oriented which I like - makes sense from an OO perspective for the Object to know how to update itself etc.

The only negative I see is that if someone is going to invest the time to use the framework like you have set up for Struts dialog, I'd rather suggest they start working with JSF. To me, their concept in the regard to how you are using ActionForms, falls more in line with JSF (and webworks)... a backing type bean that handles the operations on itself. I'm sure i'm mangling up terms I know.

My main thrust, though, is not that Struts Dialog doesn't look nice, I just think that it's very radically different from how people expect Struts to work and if people start using it that are new and start asking questions on the struts list, they will be totally lost.

--
Rick

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to