Pagination - Actions

Introduction

Pagination is based around client implementations of Composite Cache and is complex, so read that first. Pagination Actions are also client implementations of Template classes. Examples here use Convention plug-in but that's not required.

Action type Template classes Description
View List AbstractViewListActionSupport Reads and displays entire list, a page, or a single record with details, which are usually lazy loaded. May read URL parameter to set the page number or select record by index.
Find List AbstractFindBaseRecordListActionSupport,
AbstractFindIdListActionSupport,
AbstractFindIndexListActionSupport,
AbstractFindListActionSupport,
AbstractFindListMultiModeActionSupport
Creates list finder Commands, usually from a form, then redirects to a View List Action.
Save List AbstractSaveListActionSupport Updates the selected record, usually from a form, then redirects to a View List Action. May read URL parameter to set the selected item index.
Actions for pagination

View List

Introduction

View List Actions are client implementations of AbstractViewListActionSupport and various generic types must be defined and abstract functions implemented.

Generic types

View List generic types
Generic type Description
M Entry type of master list, or NA if Action displays master list.
K Type of list's primary key.
T Entry type of list.
FT Type of view helper that's a formatted version of T. This can be a class with fields with the same names as in T but all strings but additional fields for presentation calculations are common. This can be the same as T but this depends on the ValueStack for formatting and not recommended.

For example:

@InterceptorRefs({

    @InterceptorRef(value="ViewStack")

})

@Results({

    @Result(name="success", location="/views/account/AccountList.jsp")

})

public class ViewAccountListAction extends AflViewListActionSupport<NA,GenericAccountId,GenericAccountDTO,AccountSocnetDisplay> {

    ...

}

Required functions

execute only calls the superclass function but must still be implemented to have annotation.

@Action("/account/ViewAccountList")

@Override

public String execute() throws Exception {

    return super.execute();

}

formatRecord is called for each record that will be displayed to return a formatted version.

@Override

protected AccountSocnetDisplay2 formatRecord(NA selectedMaster, GenericAccountDTO record) {

    return new AccountSocnetDisplay(record);

}

getListCache should return the list cache to display.

@Override

protected ListCache<NA, GenericAccountId, GenericAccountDTO> getListCache() {

    return AccountCompositeCache.getInstance(getAflBrowserTabSession()).getMasterListCache();

}

getMasterListCache should return the master list cache, or null if this page displays the master list cache.

@Override

protected ListCache<?, ?, NA> getMasterListCache() {

    return null;

}

ViewListConfig configures how much is displayed, amongst other parameters, though ViewListConfig.makeNonPagedListConfig() and ViewListConfig.makePagedListConfig(pageSize) suffices for lists and ViewListConfig.makeSingleRecordConfig() for detailed single records.

@Override

protected ViewListConfig getViewListConfig() {

    return ViewListConfig.makePagedListConfig(20);

}

Useful optional functions

startRecordFormatting and endRecordFormatting is called before and after the loop for formatRecord and is useful for loading data for record formatting.

getViewResponse can load and format other data for the JSP page.

getItemSorter can read URL parameters ('sort' and 'ascending' by default) to create a Command that sorts the list or leave as is. As sorting requires loading the entire list, this is only recommended for full list pagination mode.

View List JSP

Introduction

The core property is formattedList, which is type FormattedListDisplay. A few uses are shown below.

List

The formattedList.list property is the page of formatted records for display. As lists typically link to a detailed single record page, the example below includes the link to set the selected item.

<TABLE class="list" >

  <s:iterator var="account" status="loopStatus" value="formattedList.list">

    <s:url var="detailpage" value="/account/ViewAccount.do2">

      <s:param name="select" value="#loopStatus.index + formattedList.pageStartIndex" />

    </s:url>

  

    <TR STYLE="cursor: pointer;" onclick="location = '<s:property value="#detailpage" />';"> 

      <TD style="width: 80px;"><s:property value="#account.service"/></TD>

      <TD style="width: 210px;"><s:property value="#account.email"/></TD>

      <TD style="width: 110px"><s:property value="#account.nickname" /></TD>

      <TD style="width: 100px"><s:property value="#account.CLI" /></TD>

      <TD style="width: 110px"><s:property value="#account.firstName" /></TD>

      <TD style="width: 110px;"><s:property value="#account.lastName" /></TD>

      <TD style="width: 50px"><s:property value="#account.status" /></TD>

      <TD style="width: 60px"><s:property value="#account.ageVerifiedFlag" /></TD>

    </TR>

  </s:iterator>

</TABLE> 

  

The formattedList property has sub-properties, such as page number, which can be used for navigation controls, such as below.

<DIV CLASS="footing_box">

  <DIV CLASS="invisibleinlinepanel" STYLE="width: 165px;">

      <s:if test="formattedList.page > 1" >

        <s:url var="backwardUrl" value="/tools/ViewContentTypeList.do2" >

          <s:param name="page" value="formattedList.page - 1" />

        </s:url>

        <s:url var="firstUrl" value="/tools/ViewContentTypeList.do2" >

          <s:param name="page" value="1" />

        </s:url>

        <BUTTON CLASS="action_button" TYPE="BUTTON" onclick="location = '<s:property value='#firstUrl' />'">First</BUTTON>

        <BUTTON CLASS="action_button" TYPE="BUTTON" onclick="location = '<s:property value='#backwardUrl' />'">Previous</BUTTON>

      </s:if>

  </DIV>

      

  <DIV CLASS="invisibleinlinepanel" STYLE="width: 630px; text-align: center;">

    <P CLASS="navigationcounterlabel">Page <s:property value="formattedList.page"/> of <s:property value="formattedList.totalPages"/></P>

  </DIV>

      

  <DIV CLASS="invisibleinlinepanel" STYLE="width: 145px;">

      <s:if test="formattedList.page < formattedList.totalPages">

        <s:url var="forwardUrl" value="/tools/ViewContentTypeList.do2" >

            <s:param name="page" value="formattedList.page + 1" />

        </s:url>

        <s:url var="lastUrl" value="/tools/ViewContentTypeList.do2" >

            <s:param name="page" value="formattedList.totalPages" />

        </s:url>

        <BUTTON CLASS="action_button" TYPE="BUTTON" onclick="location = '<s:property value='#forwardUrl' />'">Next</BUTTON>

        <BUTTON CLASS="action_button" TYPE="BUTTON" onclick="location = '<s:property value='#lastUrl' />'">Last</BUTTON>

      </s:if>

  </DIV>

</DIV>

Detailed single record

The formattedSelectedItem property is the formatted version of the selected item.

<INPUT STYLE="width: 60px;" CLASS="displayfield" value="<s:property value="formattedSelectedItem.accountId"/>" name="accountId"/>

Single record navigation

Similarly, formattedList sub-properties, can be used for navigation controls, such as below.

<DIV CLASS="footing_box">

  <DIV>

    <s:if test="formattedList.selectedIndex > 0">

      <s:url var="firstUrl" value="/tools/ViewContentType.do2">

        <s:param name="select" value="0" />

      </s:url>

      <s:url var="backwardUrl" value="/tools/ViewContentType.do2">

        <s:param name="select" value="formattedList.selectedIndex - 1" />

      </s:url>

      <BUTTON CLASS="action_button" TYPE="BUTTON" onclick="location = '<s:property value='#firstUrl' />'">First |<</BUTTON>

      <BUTTON CLASS="action_button" TYPE="BUTTON" onclick="location = '<s:property value='#backwardUrl' />'">Previous <<</BUTTON>

    </s:if>

    

    <BUTTON CLASS="action_button" TYPE="BUTTON" onclick="location = '<s:url value="/tools/ViewContentTypeList.do2" />'">Back to List</BUTTON>

    <s:if test="formattedList.selectedIndex > -1">

      <P CLASS="navigationcounterlabel" >Record <s:property value="formattedList.selectedIndex + 1" /> of <s:property value="formattedList.totalItems" /></P>

    </s:if>

    

    <s:if test="formattedList.selectedIndex > -1 && formattedList.selectedIndex < formattedList.totalItems - 1">

      <s:url var="forwardUrl" value="/tools/ViewContentType.do2">

        <s:param name="select" value="formattedList.selectedIndex + 1" />

      </s:url>

      <s:url var="lastUrl" value="/tools/ViewContentType.do2">

        <s:param name="select" value="formattedList.totalItems - 1" />

      </s:url>

      <BUTTON CLASS="action_button" TYPE="BUTTON" onclick="location = '<s:property value='#forwardUrl' />'" >Next >></BUTTON>

      <BUTTON CLASS="action_button" TYPE="BUTTON" onclick="location = '<s:property value='#lastUrl' />'" >Last >|</BUTTON>

      </s:if>

    </DIV>

</DIV>

Add mode

The existingSelection property checks if formattedList.selectedIndex is zero or more. Setting the selected index to -1 is useful for single record pages to detect an 'add' mode, such as below.

<s:if test="existingSelection">

  <s:url var="formURL" value="/tools/UpdateContentType.do2" />

</s:if>

<s:else>

  <s:url var="formURL" value="/tools/AddContentType.do2" />

</s:else>

<FORM NAME="form" ACTION="<s:property value="#formURL" />">

  ...  

</FORM>

Find List Actions

Introduction

Find List Actions are client implementations of various Template classes, one for each pagination mode, and various generic types must be defined and abstract functions implemented. There's also a special Template that chooses the pagination mode at run-time.

Pagination mode Template class
Base record list AbstractFindBaseRecordListActionSupport
Full list AbstractFindListActionSupport
Multiple AbstractFindListMultiModeActionSupport
Page by id AbstractFindIdListActionSupport
Page by index AbstractFindIndexListActionSupport
Find List Actions Template classes

Generic types

Generic type for  
Base record list Page by id Page by index Full list Multi mode Description
M M M M M Entry type of master list, or NA if Action finds master list.
K K Type of list's primary key.
T T T T Entry type of list.
F F F F F Type of form read this Action, or NullForm for no form.
Find List Actions generic types

For example:

@InterceptorRefs({

    @InterceptorRef(value="formDrivenStack")

  })

@Results({

    @Result(name="error", type="redirectAction", params = {"namespace", "/account", "actionName", "ViewAccountList"}),

    @Result(name="inout", type="redirectAction", params = {"namespace", "/account", "actionName", "ViewAccountList"}),

    @Result(name="success", type="redirectAction", params = {"namespace", "/account", "actionName", "ViewAccountList"})

})

public class FindAccountsMultiAction2 extends AbstractFindListMultiModeActionSupport<NA,GenericAccountId,GenericAccountVO,FindAccountsMultiForm> {

    ...

}

Required functions

execute functions only calls the superclass function but must still be implemented to have annotation.

@Action("/account/FindAccountsMulti")

@Override

public String execute() throws Exception {

    return super.execute();

}

Each Template class has its own version of the function for creating finder Commands. Each returns an instance of its own response class, usually created by a static function named makeSuccessResponse. Use makeFailureResponse if an error happens. Use the function named like makeSuccessWithListResponse if the query needs to be run immediately, such as treating no results as an error.

Pagination mode Create finder function Finder interfaces Response class
Base record list getBaseRecordListFinder ListFinder FindBaseRecordListResponse
Full list getFindCommand ListFinder FindListResponse
Multiple getFindCommand ListFinder, IdListFinder, and ListSizeFinder FindListMultiModeResponse
Page by id getFindCommand IdListFinder FindIdListResponse
Page by index getFindCommand ListSizeFinder FindIndexListResponse
Find List Actions create finder functions

The example below creates various finder Commands with some calling a remote service that supports pagination.

@Override

protected FindListMultiModeResponse<Object,GenericAccountId,GenericAccountVO> getFindCommand(NA selectedMaster) throws Exception {

    FindAccountsMultiForm form;

    Integer serviceId;

    

    form = getForm();

    serviceId = form.getServiceId();

    

    switch (form.getSearchCriteria()) {

    case ACCOUNT_ID: return FindListMultiModeResponse.makeSuccessFullListResponse(new AccountIdListFinder(form.getAccountId())); 

    case EMAIL: return FindListMultiModeResponse.makeSuccessFullListResponse(new EmailAccountListFinder(form.getEmail()));

    case JOINED_DATE: return FindListMultiModeResponse.makeSuccessFullListResponse(new JoinedDateRangeAccountListFinder(form.getJoinedDateRangeStart(), form.getJoinedDateRangeEnd()));

    case SERVICE_PARTIAL_NICKNAME: return FindListMultiModeResponse.makeSuccessIdListResponse(new ServicePartialNicknameAccountIdsFinder(serviceId, form.getNickname()));

    case SERVICE_PARTIAL_FORENAME: return FindListMultiModeResponse.makeSuccessIdListResponse(new ServicePartialForenameAccountIdsFinder(serviceId, form.getForename()));

    case SERVICE_PARTIAL_SURNAME: return FindListMultiModeResponse.makeSuccessIdListResponse(new ServicePartialSurnameAccountIdsFinder(serviceId, form.getSurname()));

    }

    return null;

}

getListCache should return the list cache to display.

@Override

protected ListCache<NA, GenericAccountId, GenericAccountDTO> getListCache() {

    return AccountCompositeCache.getInstance(getAflBrowserTabSession()).getMasterListCache();

}

getMasterListCache should return the master list cache, or null if this Action finds for the master list cache.

@Override

protected ListCache<?, ?, NA> getMasterListCache() {

    return null;

}

Each Template class has its own version of the function for configuring behaviour, which is whether to return an error message if no results are found and the error message to display.

Pagination mode Create finder function Response class
Base record list getFindBaseRecordListConfig .FindBaseRecordListConfig
Full list getFindListConfig FindListConfig
Multiple getFindListMultiModeConfig FindListMultiModeConfig
Page by id getFindIdListConfig FindIdListConfig
Page by index getFindIndexListConfig FindIndexListConfig
Find List Actions config functions
@Override

protected FindListMultiModeConfig getFindListMultiModeConfig() {

    FindListMultiModeConfig listConfig;

    

    listConfig = new FindListMultiModeConfig();

    listConfig.setRejectNoMatch(true);

    listConfig.setRejectMessage("No records found");

    return listConfig;

}

makeForm is required by all form processing Actions to create an initial form.

@Override

protected FindAccountsMultiForm makeForm() {

    return new FindAccountsMultiForm();

}

Useful optional functions

All versions have a function named translateFinderException, which is called if the finder Command throws an exception and returns the error message to display.

Save List Actions

Introduction

Save List Actions are client implementations of AbstractSaveListActionSupport that updates a database (or other business function) based on the selected record. Various generic types must be defined and abstract functions implemented.

Generic types

Generic type Description
M Entry type of master list, or NA is Action displays master list.
K Type of list's primary key.
T Entry type of list.
F Type of form used this Action, or NullForm no form used.
Save List generic types

For example:

@InterceptorRefs({

    @InterceptorRef(value="formDrivenStack")

  })

@Results({

    @Result(name="error", type="redirectAction", params = {"namespace", "/account", "actionName", "ViewAccount"}),

    @Result(name="input", type="redirectAction", params = {"namespace", "/account", "actionName", "ViewAccount"}),

    @Result(name="success", type="redirectAction", params = {"namespace", "/account", "actionName", "ViewAccount"})

})

public class SaveAccountProfileAction extends AbstractSaveListActionSupport<NA,GenericAccountId,GenericAccountVO,AccountProfileForm> {

    ...

}

Required functions

doSave is the purpose of the Action and returns an instance of SaveResponse, which defines what to do next. It's easiest to call one of the static functions of SaveResponse.

SaveResponse static functions for doSave
Static function Parameters Use
makeCreatedAndReloadItemSuccessResponse Created item, info message Includes DTO of just created entry, which is added to list and item is reloaded using its primary key. This is useful where list expects related data but the back-end create function just returns the base DTO.
makeCreatedItemSuccessResponse Created item, info message Includes DTO of just created entry, which is added to list.
makeDeselectResponse Set list's selected index to -1, which is an easy way to indicate pages should be in 'add' mode.
makeFailureAndReloadItemResponse Error message Sets error message and reloads item. This is useful where a concurrent update of the record by another process caused the failure and the updated record must be displayed to the user to show why.
makeFailureAndReloadListResponse Error message Sets error message then reloads entire list. This is useful where a concurrent update of the record by another process caused the failure and the updated list must be displayed to the user to show why.
makeFailureAndReloadMasterResponse Error message Sets error message as update failed, then reloads master record. This is useful where a concurrent update of the record by another process caused the failure and the updated master record must be displayed to the user to show why.
makeFailureResponse Error message Includes error message to display as update failed. This is the most common response to failure.
makeFailureResponseExistingMessages An unusual use case of aborting an Action if at least one error message has already been set.
makeInvisibleSuccessResponse Info message For unusual use cases, such as cache invalidation, where there's no apparent change in the record .
makeRemovedItemSuccessResponse Info message Sets info message and removes selected record from list.
makeSuccessAndReloadItemResponse Info message Sets info message and reloads item as there are back-end changes, such as audit fields, that need to be displayed.
makeSuccessAndReloadListResponse Info message Sets info message and reloads entire list as there are back-end changes that need to be displayed.
makeSuccessAndReloadMasterResponse Info message Sets info message and reloads item as there are back-end changes that need to be displayed.
makeUpdatedItemSuccessResponse Updated item, info message Replaces selected item with updated item and sets info message.
makeUpdatedItemSuccessSilentResponse Updated item Replaces selected item with updated item and without setting any message.

For example.

@Override

protected SaveResponse<GenericAccountVO> doSave(NA selectedMaster, GenericAccountVO selectedItem) throws Exception {

    try {

        accountAdminSocnetModel.setProfile(accountId.getServiceId(), selectedItem);

        return SaveResponse.makeSuccessAndReloadItemResponse("Customer profile for " + selectedItem.getNickname() + " edited.");

    }

    catch (RemoteServiceCommunicationFailureRuleException e) {

        return SaveResponse.makeFailureResponse("Server not responding.  Service may be shutdown.");

    }

    catch (EmailAlreadyVerifiedException e) {

        return SaveResponse.makeFailureResponse("The email address is being used by another account.");

    }

}

execute only calls the superclass function but must still be implemented to have annotation.

@Action("/account/SaveAccountProfile")

@Override

public String execute() throws Exception {

    return super.execute();

}

getListCache should return the list cache to update.

@Override

protected ListCache<NA, GenericAccountId, GenericAccountDTO> getListCache() {

    return AccountCompositeCache.getInstance(getAflBrowserTabSession()).getMasterListCache();

}

getMasterListCache should return the master list cache, or null if this page updates the master list cache.

@Override

protected ListCache<?, ?, NA> getMasterListCache() {

    return null;

}

getSaveListConfig can usually be the following unless URL parameter names need to changed.

@Override

protected SaveListConfig getSaveListConfig() {

    return new SaveListConfig();

}

makeForm is required by all form processing Actions to create an initial form.

@Override

protected SaveAccountProfileForm makeForm() {

    return new SaveAccountProfileForm();

}