ajaxgrailsrendergspgrails-controller

Update template inside a view rendered from another controller


I am looking for the way to refresh a template inside a view rendered from another controller than the template's controller, I mean:

I got two controllers AdminController & UserController. And two gsps /admin/listUsers & /user/_searchResult.

Then a want to render view listUsers who have inside the template _searchResult and all right.

Now, i want to refresh the template _searchResult, but cant find how. I tryed calling render(view:"/admin/listUsers", template:"/user/_searchResult", model:[searchResult:result])


AdminController.groovy

@Secured(['ROLE_ADMIN'])
def listUsers(){
    //...
}

UserController.groovy

@Secured(['ROLE_ADMIN'])
def search(){
    //search users for the givven params and send result by chain if there's an action or update a template if it's needed
    //in my case this method need to update the template _searchResult
}

@Secured(['ROLE_ADMIN'])
def searchResult(){
    //...
    [searchResult:result]
}

listUsers.gsp

//...
<formRemote name="searchForm" url="[action:"search", controller:"user"]">
     //Some fields for the search
     //I need to place here some hidden inputs to send which 
     //template i want to update or action to redirect
</formRemote>
<g:render template="/user/_searchResult"/>

//...

_searchResult.gsp

//Just itterate and print the search result in a table

I hope I have explained the problem correctly, thanks!


Solution

  • I don't think I entirely understand your question, but I think the source of your confusion is that the way you are naming things doesn't follow regular conventions and you're not using the right tools for the job. Let me explain...

    The methods on Controllers are called Actions. They send some data (the Model) to a View to be rendered into HTML. Views can be composed from smaller, reusable fragments called Templates. (sorry if I sound like I'm being condescending here, but I'm just trying to make sure we're all on the same page).

    Now, what you've called templateA is actually a View, not a Template. You're correct that templateA (your View) can call templateB to render some markup, but then having the templateB try to call a method on another Controller doesn't make sense. That's not how things flow.

    If you have some logic that needs to be executed after you've sent your Model to the View, you want to use a Tag Library (http://grails.org/doc/latest/guide/theWebLayer.html#taglibs).

    To summarise, here's a quick recap.

    1. A request should only call one Action, which sends the model to only one view.
    2. If you need to reuse logic between Controllers, move that code to a Service.
    3. If you need to reuse markup between Views, move that markup to a Template.
    4. If you have logic that you want to have executed after you've sent the Model to the View, use a Tag Library.

    Hopefully this will point you in the right direction.

    --- UPDATE ---

    OK, with the real code I can see better what you're trying to achieve. Firstly, as you're using the <g:formRemote> tag, you should have a good read of the docs at http://grails.org/doc/latest/ref/Tags/formRemote.html to understand what it does.

    What you will have here is 2 separate requests. The first will be a regular page load by your browser, which is handled by the listUsers() action. Once the page is then finished loading, the user will enter a search term and hit the submit button. This will fire off a second ajax request, which will be handled by the search() action. This action could use the _searchResult.gsp template to render a HTML table to display the search results. When the browser get this, it will insert it into the DOM where you've told it to put it using the "update" attribute of the <g:formRemote> tag.

    The important thing here is that from the server's perspective, these are 2 separate requests that are completely independent. They both first call an action, then send a model (a Map containing some data) to a view, which renders/merges the data with HTML and sends it back to the browser.

    The difference between the 2 is that the first is a complete page load by the browser, whereas for the second request, the browser only loads a small chunk of HTML (the search results table) and updates the page content without reloading it.

    So your code would look more like this...

    AdminController.groovy

    @Secured(['ROLE_ADMIN'])
    def listUsers() {
        render(view:"/admin/listUsers")
    }
    

    listUsers.gsp

    <g:formRemote name="searchForm" update="insertSearchResultsHere"
              url="[controller: 'user', action:'search']">
        <input name="searchTerm" type="text" />
    </g:formRemote>
    <div id="insertSearchResultsHere"></div>
    

    UserController.groovy

    @Secured(['ROLE_ADMIN'])
    def search() {
        // use the search term to get a List<User>
        render(template: "/user/searchResult", model: [users: users])
    }
    

    _searchResult.gsp

    <table>
        <g:each var="user" in="${users}">
            %{-- Iterate through your search results --}%
        </g:each>
    </table>