Grails sortableColumn is refreshing page instead of container

32 Views Asked by At

I have a grails application that was upgraded to 5.3.2 and it was using util:remoteSortableColumn for the sorts on the table. Only replacement I found was g:sortableColumn, so I've been trying to get sort to work properly. However it refreshes the page using the template instead of staying on the page and updating the storage container. I have pagination that works fine and they go through the same code. What have I done wrong?

Search.gsp

<%--
  Created by IntelliJ IDEA.
  User: GinormousMammal
  Date: 9/27/13
  Time: 8:52 AM
  To change this template use File | Settings | File Templates.
--%>

<%@ page import="com.moo.uc.health.Contact" contentType="text/html;charset=UTF-8" %>

<!DOCTYPE html>
<html>
<head>
    <g:set var="pageLayout" value="health/main"/>
    <sec:ifAllGranted roles="ROLE_HEALTH_ADMIN">
        <g:if test="${session['adminView']}">
            <g:set var="pageLayout" value="health/admin"/>
        </g:if>
    </sec:ifAllGranted>
    <g:javascript library="jquery" />
    <meta name="layout" content="${pageLayout}">
    <title>Search Communications</title>
</head>

<body>
<content tag="breadcrumb">
    <ul class="breadcrumb">
        <li>
            <g:link uri="/" action="index"><g:message code="uc.home.label"/></g:link>
            <span class="divider">/</span>
        </li>
        <g:if test="${sec.ifAllGranted(roles: "ROLE_HEALTH_ADMIN")}">
            <li>
                <g:link controller="health" action="admin"><g:message code="health.admin.label"/></g:link>
                <span class="divider">/</span>
            </li>
            <li>
                <g:link controller="healthProfile"><g:message code="health.admin.profile.maintenance.label"/></g:link>
                <span class="divider">/</span>
            </li>
            <g:if test="${session['productId']}">
                <li>
                    ${com.moo.uc.health.HealthProduct.get(session['productId'])?.name}
                    <span class="divider">/</span>
                </li>
            </g:if>
            <li class="active"><g:message code="health.admin.search.label"/></li>
        </g:if>
        <g:else>
            <li>
                <g:link controller="health" action="index"><g:message code="health.home.label"/></g:link>
                <span class="divider">/</span>
            </li>
            <g:if test="${session['productId']}">
                <li>
                    ${com.moo.uc.health.HealthProduct.get(session['productId'])?.name}
                    <span class="divider">/</span>
                </li>
            </g:if>
            <li class="active"><g:message code="health.communication.search.label"/></li>
        </g:else>
    </ul>
</content>
<content tag="main-content">
    <g:if test="${errors != null && errors.size() > 0}">
        <div class="row-fluid">
            <div class="alert alert-error">
                <button type="button" class="close" data-dismiss="alert">×</button>
                <h4>There were errors with the search parameters</h4>
                <ul>
                    <g:each in="${errors}">
                        <li>${it}</li>
                    </g:each>
                </ul>
            </div>
        </div>
    </g:if>
    <div class="row-fluid">
        <div class="span12">
            <g:formRemote name="communicationSearchForm" class="uc-form"
                          url="[controller: 'healthCommunication', action: 'filterAjax']" role="form"
                          update="searchResultsContainer"
                          onSuccess="jQuery('#searchButton').removeClass('disabled').text('Search');">

                <g:if test="${session['productId']}">
                    <g:hiddenField name="healthProduct.id" value="${session['productId']}"/>
                </g:if>

                <div class="well">
                    <div class="row-fluid">
                        <div class="span12">
                <g:link controller="healthCommunication" action="createSelectTemplate"
                        class="btn btn-mini btn-primary pull-right"><span
                        class="icon-plus icon-white"></span> New Communication</g:link>
                </div>
            </div>
                <div class="row-fluid">
                    <div class="span12">

                        <div class="span4 ${hasErrors(bean: healthCommunicationSearchFormInstance, field: 'policyNumber', 'error')}">
                            <label for="policyNumber">Policy Number</label>
                            <input id="policyNumber" type="text" name="policyNumber"
                                   value="${healthCommunicationSearchFormInstance?.policyNumber}"/>
                        </div>


                        <div class="span4 ${hasErrors(bean: healthCommunicationSearchFormInstance, field: 'clientName', 'error')}">
                            <label for="clientName">Client Name</label>
                            <input id="clientName" type="text" name="clientName"
                                   value="${healthCommunicationSearchFormInstance?.clientName}"/>
                        </div>

                    </div>
                </div>

                <div class="row-fluid">

                    <div class="span4 ${hasErrors(bean: healthCommunicationSearchFormInstance, field: 'startDate', 'error')}">
                        <label for="startDate">Start Date</label>
                        <input id="startDate" type="text" name="startDate"
                               value="${healthCommunicationSearchFormInstance?.startDate ? formatDate(format: 'MM/dd/yyyy', date: healthCommunicationSearchFormInstance.startDate) : ''}"/>
                        <script type="text/javascript">
                            jQuery(document).ready(function () {
                                $("#startDate").datepicker();
                            });
                        </script>
                    </div>

                    <div class="span4 ${hasErrors(bean: healthCommunicationSearchFormInstance, field: 'endDate', 'error')}">
                        <label for="endDate">End Date</label>
                        <input id="endDate" type="text" name="endDate"
                               value="${healthCommunicationSearchFormInstance?.endDate ? formatDate(format: 'MM/dd/yyyy', date: healthCommunicationSearchFormInstance.endDate) : ''}"/>
                        <script type="text/javascript">
                            jQuery(document).ready(function () {
                                $("#endDate").datepicker();
                            });
                        </script>
                    </div>
                </div>

                <div class="row-fluid">
                    <div class="span6">
                        <label for="authorsTags">Author</label>

                        <ul id="authorsTags" class="${invalid ? 'uc-jquery-tagit-error' : ''}">
                            <g:if test="${healthCommunicationSearchFormInstance?.authors}">
                                <g:each in="${healthCommunicationSearchFormInstance.authors}">
                                    <li ${it.id ? 'data-id="' + it.id + '"' : ''}
                                            data-username="${it.username}"
                                            data-firstname="${it.firstName}"
                                            data-lastname="${it.lastName}">${it}</li>
                                </g:each>
                            </g:if>
                        </ul>

                        <script type="text/javascript">
                            jQuery(document).ready(function () {
                                new UserAutoComplete(
                                        $("#authorsTags"),
                                        {
                                            fieldName: "authors"
                                        }
                                ).render();
                            });
                        </script>
                    </div>
                </div>

                <div class="row-fluid">
                    <div class="span12">
                        <div class="pull-right">
                            <div class="span12">
                                <button id="searchButton" type="submit" class="btn btn-primary"
                                        onclick="jQuery('#searchButton').addClass('disabled').text('Searching...');"><g:message
                                        code="spiaForm.button.search.label" default="Search"/></button>
                                <g:link controller="healthCommunication" action="search"
                                        class="btn btn-danger">Clear</g:link>
                            </div>
                        </div>
                    </div>
                </div>
            </g:formRemote>
        </div>
        </div>
    </div>

    <div class="row-fluid">
        <div class="span12">
            <div id="searchResultsContainer" class="well" style="background-color: #fafafa;">
                <g:render template="/health/communication/searchResults" model="[searchResults: searchResults]"/>
            </div>
        </div>
    </div>
</content>
</body>
</html>

_searchResults.gsp

<g:if test="${searchResults == null}">

</g:if>
<g:elseif test="${searchResults.isEmpty()}">
    <div class="alert alert-info">
        <button type="button" class="close" data-dismiss="alert">×</button>
        <h4>No Results Found</h4>
        <p>There are no communications to display given the search parameters above.</p>
    </div>
</g:elseif>
<g:else>
    <div class="row-fluid">
        <div class="span12">
            <h4>
                Search Results <small> - ${searchResultsCount} communications(s) found</small>
            </h4>
        </div>
    </div>
    <div class="row-fluid">
        <div class="span12">
            <table class="table table-striped table-bordered">
                <thead>
                <tr>
                    <g:sortableColumn params="${healthCommunicationSearchFormInstance.searchParams()}" property="policyNumber" title="${message(code: 'health.profile.search.person.label', default: 'Policy Number')}" />
                    <g:sortableColumn params="${healthCommunicationSearchFormInstance.searchParams()}" property="subject_email" title="${message(code: 'health.profile.search.subject_email.label', default: 'Subject')}" />
                    <th>Form</th>
                    <g:sortableColumn params="${healthCommunicationSearchFormInstance.searchParams()}" property="communicationStatus" title="${message(code: 'health.profile.search.communicationStatus.label', default: 'Status')}" />
                    <g:sortableColumn params="${healthCommunicationSearchFormInstance.searchParams()}" property="dateCreated" title="${message(code: 'health.profile.search.dateCreated.label', default: 'Date')}" />
                    <th> </th>
                </tr>
                </thead>
                <tbody>
                <g:each in="${searchResults}">
                    <tr>
                        <td>
                            ${it.policyNumber}
                        </td>
                        <td>
                            ${it.subject_email}
                        </td>
                        <td>
                            ${it.formTemplate.form.name}
                        </td>
                        <td>
                            ${it.communicationStatus}
                        </td>
                        <td>
                            ${it.date_email ? formatDate(format:'MM/dd/yyyy HH:mm a',date:it.date_email) : ''}
                        </td>
                            <td>
                                <div class="btn-group">
                                    <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
                                        <span class="icon-wrench"></span>
                                        <span class="caret"></span>
                                    </a>
                                    <ul class="dropdown-menu pull-right">

                                        <li>
                                            <g:link controller="healthCommunication" action="show" id="${it.id}"><i class="icon-eye-open"></i> View</g:link>
                                        </li>

                                    </ul>
                                </div>
                            </td>
                    </tr>
                </g:each>
                </tbody>
            </table>
        </div>
    </div>
    <div class="row-fluid">
        <div class="span12">
            <uc:remotePaginate controller="healthCommunication" action="paginateAjax" total="${searchResultsCount}" update="searchResultsContainer" params="${healthCommunicationSearchFormInstance.searchParams()}" max="10"/>
        </div>
    </div>
</g:else>

Controller

package com.moo.uc.health

import com.moo.uc.health.form.*
import com.moo.uc.ldap.LdapPersonService
import com.moo.uc.life.LifeCommunicationStatus
import com.moo.uc.user.UserProfileService
import grails.converters.JSON
import grails.plugin.springsecurity.annotation.Secured
import grails.gorm.transactions.Transactional
import org.springframework.dao.DataIntegrityViolationException

import java.text.ParseException
import java.text.SimpleDateFormat


@Secured(['ROLE_HEALTH_USER', 'ROLE_HEALTH_ADMIN'])
class HealthCommunicationController {

    def springSecurityService
    HealthCommunicationService healthCommunicationService = new HealthCommunicationService()
    LdapPersonService ldapPersonService = new LdapPersonService()
    UserProfileService userProfileService = new UserProfileService()

    def index() {
        redirect(action: "search", params: params)
    }

    def search() {
        CommunicationSearchForm model = new CommunicationSearchForm()
        model.authors = [springSecurityService.getCurrentUser()]
        render(
                view: '/health/communication/search',
                model: [
                        healthCommunicationSearchFormInstance: model
                ]
        )
    }

    @Transactional
    def filterAjax(CommunicationSearchForm communicationSearch) {
        def pattern = "MM/dd/yyyy"
        params.max = Math.min(params.max as Long ?: 10, 100)
        log.error("filterAjax ${params.toQueryString()}")
        if (!params.sort && !params.order) {
            params.sort = 'dateCreated'
            params.order = 'desc'
        }

        if (params?.authors) {
            params.authors.username.values().each {
                communicationSearch.authors.add(userProfileService.findOrCreateUser(it))
            }
        }

        if (params?.startDate) {
            try {
                log.error("paginateAjax startDate ${params?.startDate}")
                def startDate = Date.parseToStringDate(params?.startDate).format("MM/dd/yyyy")
                def storeStartdate = new SimpleDateFormat(pattern).parse(startDate)
                log.error("paginateAjax startDate ${storeStartdate}")
                communicationSearch.startDate = storeStartdate
            } catch (ParseException parseException) {
                log.debug parseException.toString()

            } finally {
                log.error("paginateAjax ${communicationSearch?.startDate}")
            }
        }

        if (params?.endDate) {
            try {
                log.error("paginateAjax endDate ${params?.endDate}")
                def endDate = Date.parseToStringDate(params?.endDate).format("MM/dd/yyyy")
                def storeEnddate = new SimpleDateFormat(pattern).parse(endDate)
                log.error("paginateAjax endDate ${storeEnddate}")
                communicationSearch.endDate = storeEnddate
            } catch (ParseException parseException) {
                log.debug parseException.toString()

            } finally {
                log.error("paginateAjax ${communicationSearch?.endDate}")
            }
        }

        List<Communication> results = Communication.createCriteria().list(params, communicationSearch.searchCriteria())

        render(
                template: '/health/communication/searchResults',
                model: [
                        healthCommunicationSearchFormInstance: communicationSearch,
                        searchResults                        : results,
                        searchResultsCount                   : results.totalCount
                ]
        )
    }

    @Transactional
    def paginateAjax(CommunicationSearchForm communicationSearch) {
        def pattern = "MM/dd/yyyy"
        params.max = Math.min(params.max as Long ?: 10, 100)
        log.error("paginateAjax ${params.toQueryString()}")
        if (!params.sort && !params.order) {
            params.sort = 'dateCreated'
            params.order = 'desc'
        }


        if (params?.authors) {
            params.authors.username.values().each {
                communicationSearch.authors.add(userProfileService.findOrCreateUser(it))
            }
        }

        if (params?.startDate) {
            try {
                log.error("paginateAjax startDate ${params?.startDate}")
                def startDate = Date.parseToStringDate(params?.startDate).format("MM/dd/yyyy")
                def storeStartdate = new SimpleDateFormat(pattern).parse(startDate)
                log.error("paginateAjax startDate ${storeStartdate}")
                communicationSearch.startDate = storeStartdate
            } catch (ParseException parseException) {
                log.debug parseException.toString()

            } finally {
                log.error("paginateAjax ${communicationSearch?.startDate}")
            }
        }

        if (params?.endDate) {
            try {
                log.error("paginateAjax endDate ${params?.endDate}")
                def endDate = Date.parseToStringDate(params?.endDate).format("MM/dd/yyyy")
                def storeEnddate = new SimpleDateFormat(pattern).parse(endDate)
                log.error("paginateAjax endDate ${storeEnddate}")
                communicationSearch.endDate = storeEnddate
            } catch (ParseException parseException) {
                log.debug parseException.toString()

            } finally {
                log.error("paginateAjax ${communicationSearch?.endDate}")
            }
        }

        List<Communication> results = Communication.createCriteria().list(params, communicationSearch.searchCriteria())

        render(
                template: '/health/communication/searchResults',
                model: [
                        healthCommunicationSearchFormInstance: communicationSearch,
                        searchResults                        : results,
                        searchResultsCount                   : results.totalCount
                ]
        )
    }

I have tried searching for solutions as well as tweaking code in ways to try to get it to work. Expecting the column to sort and update in the container instead of refreshing onto the template.

1

There are 1 best solutions below

0
Daniel On

I think the only thing you're doing wrong is expecting sortableColumn to generate an ajax request. It doesn't; it's implemented as a full page refresh.

You have your own implementation (or are using a third party one?) of pagination, via <uc:remotePagination>. You will need to do something similar for column sorting, if you need that capability.