Search code examples
javascriptjquerygrailsgsp

Grails, javascript don't respond after rendering a template


I have a page that maintain a couple of lists: products and offers. Both list is constructed in their own templates. First, the product list can be filtered by using a select. When select is changed, the product list is filtered and the template is rendered. The offer list is rendered when you click on a certain point in the product list. Now, When the page is first updated, when you first enter the page or by refresh, then you can click on the product and the offer list will be updated. Then if you change the SELECT so the product list is filtered, then you can't get the offer list updated any more until you make a refresh of the page first.

The scripts are located at the header of the page and looks like this:

<script type="text/javascript">

// Update product list:
    function availableProducts(){
    $.ajax({
            url:'${g.createLink( controller:'ordersAndStore', action:'availableProducts' )}',
            data: [sawMill],
            type: 'get'
        }).success( function ( data ) { $( '#divToUpdate' ).html( data ); });
    }

// Updates offer list:
    $( document ).ready( function() {
        $( '.offers' ).click( function ( event ){
            $.ajax({
                url: '${g.createLink( controller:'ordersAndStore', action:'listOffers' )}',
                data: {id:this.id},
                type: 'get'
            }).success( function ( data ) { $( '#offerList' ).html( data );     });
        });
    });

</script>    

Here is the most important part of the page:

<body>
    <g:render template="/menue"/>
    <g:set var="entityName" value='Product' />
    <div id="scroll1 class scrolls">
        <div id="list-prodBuffer" class="content scaffold-list" role="main">
            <h1><g:message code="default.list.label" args="[entityName]" /></h1>
            <g:if test="${flash.message}">
                <div class="message" role="status">${flash.message}</div>
            </g:if>
            <g:form action="createOffer">
                <div id="selectMill">
                    Select mill:
                        <g:select name="sawMill" from="${millList}" value="" onchange="availableProducts(sawMill)" noSelection = "${['':'All']}" />
                </div>
                <g:render template="AvailableProductData" model="[prodBuffer:prodBuffer]"/>
            </g:form>
        </div>
    </div>

    <g:render template="ListOffers" model="[offerDetails:offerDetails]"/>

</body>

Then we have the two templates: _AvailableProductData.gsp and _ListOffers.gsp

<!-- _AvailableProductData -->
<div id="divToUpdate">
    <table>
        <thead>
            <tr>
                <g:sortableColumn property="Id" title='Id' />
                <g:sortableColumn property="sawMill" title='Mill' />
                <g:sortableColumn property="product" title='Product' /> 
                <g:sortableColumn property="volumeAvailable" title='Avail' />
            </tr>
        </thead>
        <tbody>

            <g:each in="${prodBuffer}" status="i" var="pb"> 
                <tr  class="${ (i % 2) == 0 ? 'even': 'odd'}">
                    <td><g:link action="edit_prodbuffer" id="${pb.id}">${pb.id}</g:link></td>
                    <td>${pb.sawMill}</td>
                    <td>${pb.product}</td>
                    <td>${pb.volumeAvailable}</td>
                </tr>
            </g:each>

        </tbody>
    </table>
</div>

<!-- _ListOffers -->
<div id="offerList">
    <g:if test = "${offerDetails != null}"> 
        <g:set var="entityName" value='Offer' />
        <div id="list-offers" class="content scaffold-list" role="main">
            <h1><g:message code="default.list.label" args="[entityName]" /></h1>
            <table style="width:100%">
                <thead>
                    <tr>
                        <g:sortableColumn property="product" title='Product' />
                        <g:sortableColumn property="sawMill" title='Sawmill' />
                    </tr>
                </thead>
                <tbody>
                    <g:each in="${offerDetails}" status="i" var="od">
                        <tr class="${ (i % 2) == 0 ? 'even': 'odd'}">
                            <td><g:link class="edit" action="edit" resource="offerDetail" id="${od?.id}"> ${od.product?.encodeAsHTML()}</g:link></td>
                            <td>${od.offerHeader.sawMill}
                        </tr>
                    </g:each>
                </tbody>
            </table>
        </div>
    </g:if>
</div>

Then we have the controller:

@Secured(['ROLE_ADMIN','ROLE_SALES', 'ROLE_SUPPLIER'])
class OrdersAndStoreController {
    def springSecurityService
    def list(Integer max) { 

        System.out.println("Controller List <<<<<<<<")

        def orders = Orders.list()
        def offerDetails = null//OfferDetail.list()

        def List<String> millList = getMills()
        [orders: orders, prodBuffer: getBufferList(), offerDetails: offerDetails, millList: millList, selectedMill:false]
   }

    def availableProducts() {

        System.out.println(params)

        def List<ProdBuffer> prodBuffer = getBufferList()
        render(template:"AvailableProductData", model:[prodBuffer: prodBuffer])
    }

    def listOffers(){
        System.out.println("#--#"+params)

        def User user
        user = springSecurityService.isLoggedIn() ? springSecurityService.getCurrentUser() : null
        def us = user.getUserSettings()
        def roles = springSecurityService.getPrincipal().getAuthorities()
        for(def role in roles){ if((role.getAuthority() == "ROLE_ADMIN") || (role.getAuthority() == "ROLE_SALES")){
                def List<OfferDetail> offerDetails = getOfferList()
                System.out.println("OfferDetail filtered count: "+offerDetails.count) 
                def orders = Orders.list()
                render(template:"ListOffers", model:[offerDetails: offerDetails])
            }
        }
    }

Would be nice if someone could find any glitch in my code here that could cause my problem.


Solution

  • I think I have it working as per your description after a few changes to a couple of gsps.

    _ListOffers.gsp

    Just remove the enclosing div <div id="offerList">, the first and last line.

    index.gsp

    Main changes here are adding <div id="offerList"></div> to the foot of the body and changing how the javascript works when rendering an offer by clicking on the .offers class. We're now using .on see this question for a few good explanations. We have to use this because we're re-rendering the product list after each drop down change.

    <!DOCTYPE html>
    <html>
    <head>
        <meta name="layout" content="main" />
        <g:set var="entityName" value="${message(code: 'product.label', default: 'Product')}" />
        <title><g:message code="default.list.label" args="[entityName]" />    </title>
    
    <style>
        .offers {
        color: #ff0000
        }
    </style>
    <script type="text/javascript">
        function availableProducts(){
            $.ajax({
                url:'${g.createLink( controller:'product', action:'availableProducts' )}',
                    data: [supplier],
                    type: 'get'
            }).success( function ( data ) { $( '#divToUpdate' ).html( data ); });
        }
    
    
        $( document ).ready( function() {
            $( document ).on( 'click', '.offers', function( event ){
                $.ajax({
                    url: '${g.createLink( controller:'product', action:'listOffers' )}',
                        data: {id:this.id},
                        type: 'get'
                }).success( function ( data ) { $( '#offerList' ).html( data );     });
            });
        });
    
    </script>    
    </head>
    <body>
    <a href="#list-product" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a>
    <div class="nav" role="navigation">
        <ul>
            <li><a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li>
            <li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li>
            </ul>
        </div>
        <div id="list-product" class="content scaffold-list" role="main">
            <h1><g:message code="default.list.label" args="[entityName]" /></h1>
        <g:if test="${flash.message}">
            <div class="message" role="status">${flash.message}</div>
        </g:if>
        <div id="selectMill">
            Select mill:
            <g:select name="supplier" from="${millList}" value="" onchange="availableProducts(supplier)" noSelection = "${['':'All']}" />
        </div>
    
        <g:render template="AvailableProductData" model="[product:product]"/>
        <div class="pagination">
            <g:paginate total="${productCount ?: 0}" />
        </div>
    
    
            <div id="offerList"></div>
    </div>
    </body>
    </html>