0

I've written an API that started life as a bunch of json request response apis, using vertx an inmemory db model. Works fine.

Then someone wanted nice HTML display to see some of the content in that app.

So I created a Grails app to try and do the simple scaffolding views for me - but of course there's no domain model per se.

I created a remoteRequestController - that calls a service that does a JSON get on my 'core app' and converts the data into objects.

So for my listing requests - I do a GET and get a json array back. I convert that a List of local 'non domain' objects and construct a graph in objects of the result.

My service returns the list to my Controller - which tries to put the map to a view.

I've tried to follow grails convention - my Controller is called 'RemoteServiceRequestController', which invokes a RemoteServiceRequestService (does the remote get/reponse, and format into objects) - and the view is in 'remoteServiceRequest/list' using a variable like so 'remoteServiceRequestList'

In that gsp I reference an item from the model map 'remoteServiceRequestList'

<!DOCTYPE html>
<html>
    <head>
        <meta name="layout" content="main" />
        <g:set var="entityName" value="${message(code: 'request.label', default: 'ServiceRequest')}" />
        <title><g:message code="default.list.label" args="[entityName]" /></title>
    </head>
    <body>
        <a href="#list-customer" 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-request" 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>
            <p> "got map as ${params}: and" + "list as ${remoteServiceRequestCount}"</p>
            <f:table collection="${remoteServiceRequestList}" />

            <div class="pagination">
                <g:paginate total="${remoteServiceRequestCount ?: 0}" />
            </div>
        </div>
    </body>
</html>

In my controller I try and do a respond like this

def list () { 
.... 
   List<Request> rlist = remoteRequestService.bindJsonToServiceRequestList(json)

        //return either an empty list or result of query, view will convert list to
        println "remote list returned $rlist, size :${rlist.size()}"
        //response rlist, [model: [remoteServiceRequestCount: rlist.size()] ]
        //Map model =  [remoteServiceRequestList: rlist, remoteServiceRequestCount: rlist.size()]
        Map model = ["remoteServiceRequestList": ["will","marian"]]
        //render (view:'list', model:model )
        //response rlist, [model: [remoteServiceRequestInstanceCount: rlist.size(), remoteServiceRequestInstanceList: rlist]]
        ModelAndView mv = new ModelAndView()
        mv.addAllObjects(model)
        mv
    }

As you can see from comments tried several ways with nill effect. The view will not render the response as HTML as I think the view automatically assumes the variable is a list of domain objects. The list is constructed ok, and I get the values I expect - but the object array I build is just from classes from src/main/groovy.

I can't seem to access the model map variables in the GSP view (unless they are domain objects), which is not what I need here.

The response method is very pernickety, and tries to get clever internally looking at the type and content - and building an variable in the map of a specific name (logically something like <domainClass>List - I've tried to set this explicitly with my remote result list, but I can't get it to render the data in the GSP view.

Can I use GSP to render HTML for variables held in the model map where the data objects are not beans (such as my array of requests) and if so how do i do that ? (I could make the objects into beans but not sure if that's going to help or not).

Otherwise I'm going to have to do some horrible raw html processing and just use render to return that to browser - rather than using the GSP plumbing to do most of the hard work for me.

I'd hoped this was going to be straight forward and its turned into a nightmare fighting the framework.

I'm not really a HTML presentation genius (which is why the scaffolding would have been good enough) - but stuck with this inability to render the response from data in map in gsp view.

How might this be made to work - or is there an alternative that will be good enough if I could have got scaffolds to work?

halfer
  • 19,824
  • 17
  • 99
  • 186
WILLIAM WOODMAN
  • 1,185
  • 5
  • 19
  • 36

1 Answers1

0

Question Can i use GSP to render HTML for variables held in the model map where the data objects are not beans (such as my array of requests) and if so how do i do that ?

Yes.

The project at https://github.com/jeffbrown/williamwoodmansample shows a very simple example of that.

https://github.com/jeffbrown/williamwoodmansample/blob/master/grails-app/controllers/williamwoodmansample/WoodmanDemoController.groovy

package williamwoodmansample

class WoodmanDemoController {

    def index() {
        [remoteServiceRequestList: ['will', 'marian']]
    }
}

https://github.com/jeffbrown/williamwoodmansample/blob/master/grails-app/views/woodmanDemo/index.gsp

<html>
<head>
    <meta name="layout" content="main"/>
</head>

<body>
<h1>Names</h1>
<ul>
    <g:each var="name" in="${remoteServiceRequestList}">
        <li>${name}</li>
    </g:each>
</ul>
</body>
</html>
Jeff Scott Brown
  • 26,804
  • 2
  • 30
  • 47
  • I know that this is not what you asked about but there are some aspects of the `fields` plugin which make assumptions about instances being domain classes. Above is an answer to the question as asked though. – Jeff Scott Brown Oct 02 '18 at 00:26
  • let me have another go jeff - i tried to use the respond method and got into all sorts of difficulties and trying to fine the gsp model name for variable like 'respond xxxList, [model:xxxCount: ' the rule looks like it takes the xxx name with List and should produce an xxxList variable in the GSP - but this was null for me, but the List i sent had real values. I'll do a basic version with no respond method and see what happens – WILLIAM WOODMAN Oct 02 '18 at 09:24
  • so Jeff - i tried again - setup new controller action - if i pass the map back directly - the simple gsp renders the names. when i change to use respond - i get empty response. def basic () { //[remoteServiceRequestList: ["will", "marian"]] println "basic : using response to return model" respond( ["will", "marian"]) } – WILLIAM WOODMAN Oct 02 '18 at 13:40
  • when you use respond - what exactly does this put as the variable into the map for the view ? works with direct name in the map - but not with respond with a list by itself ? – WILLIAM WOODMAN Oct 02 '18 at 13:42
  • This is a whole new question than the original one. It isn't entirely clear what you really need to accomplish but since you mentioned `respond( ["will", "marian"])` specifically, https://github.com/jeffbrown/williamwoodmansample/commit/671d4a2110a0904641aa1467b04f6fc0fc7ac91d shows how to do that.The model variable naming conventions are described in our docs at http://docs.grails.org/3.3.8/ref/Controllers/respond.html. – Jeff Scott Brown Oct 02 '18 at 13:45
  • if i respond like this i get no names def list = ["will", "marian"] respond( list) //, model : [remoteServiceRequestList: list]) if you remove the comment line and put the named variable into model map then the gsp will render correctly. grails v3.3.8 – WILLIAM WOODMAN Oct 02 '18 at 13:48
  • I am sorry I can't help you. Best of luck. – Jeff Scott Brown Oct 02 '18 at 13:52
  • the calculated model variable is not really clear. In the example your return Book.list() - so its implied that constructed variable is bookList. in this example im just returned a list of string (no domain class) - so what is respond assuming the stub string is xxxList? is it stringList? – WILLIAM WOODMAN Oct 02 '18 at 13:52
  • Yes, `stringList` as shown in a commit linked above (https://github.com/jeffbrown/williamwoodmansample/commit/671d4a2110a0904641aa1467b04f6fc0fc7ac91d) – Jeff Scott Brown Oct 02 '18 at 13:53
  • is it just looking at the variable type in the array and using the class.simple name as the stem in this case ? – WILLIAM WOODMAN Oct 02 '18 at 13:53
  • In short, yes. Some of the relevant work is done at https://github.com/grails/grails-core/blob/6d2949b3aa120da9d6c568c6cc4665eaa819d9b3/grails-plugin-rest/src/main/groovy/org/grails/plugins/web/rest/render/html/DefaultHtmlRenderer.groovy#L92. – Jeff Scott Brown Oct 02 '18 at 13:57
  • yes got to the same point i think. if you post a mixed list (Integer, String) how does it try and cosntruct the model name for that ? – WILLIAM WOODMAN Oct 02 '18 at 13:57
  • i added an integer into the list and the the variable no longer renders - so a new name has been constructed – WILLIAM WOODMAN Oct 02 '18 at 14:01
  • anyway in my original example i was betting a List from my remote json processing - so the implicit variable was going to serviceRequestList which is why nothing rendered. Maybe safest to just always force into model with name of your choice - as you might be able to guarantee resolving the gsp variable that way – WILLIAM WOODMAN Oct 02 '18 at 14:05
  • yes - so i tried to pass my list with an explicit name 'srList' and that is found in the map - however the fields plugin - doesnt seem to render that - i think it must be assuming the variable to be domainObjects or beans - so unless i turn my class into one or other of these - it cant be rendered by the fields plugin – WILLIAM WOODMAN Oct 02 '18 at 14:34
  • "if you post a mixed list (Integer, String) how does it try and cosntruct the model name for that ?" - It uses the first element in the collection. – Jeff Scott Brown Oct 02 '18 at 14:49
  • " i think it must be assuming the variable to be domainObjects or beans - so unless i turn my class into one or other of these - it cant be rendered by the fields plugin" - That is not true. – Jeff Scott Brown Oct 02 '18 at 14:50
  • I would repeat the comment that I left on another question you posted at https://stackoverflow.com/questions/49389879/grails-v3-3-3-json-views-1-2-7-getting-stack-overflow-when-doing-a-deep-rende. There is a lot of stuff in your code sample that is unrelated and complicates figuring out whatever you are actually asking about (which isn't clear to me). – Jeff Scott Brown Oct 02 '18 at 14:56
  • jeff - both projects (the core api server using vertx/groovy) and the newer ticketAdapterUI (grails app ) https://github.com/woodmawa/ticketAdapterUI is here. what i was trying to do - was put a simple html UI ontop of the other project that when it started didnt need any HTML - its a simple processing engine for a SDN/NFV demo that took events - and converted them to json calls on different itil based systems. during this i was asked to exend the app to so that you could see some of the data as html. I was trying to do that without rewriting the base app and got stuck in grails forms – WILLIAM WOODMAN Oct 02 '18 at 15:21
  • i wanted a controller that just read the API data - and constructed some local types - and post those to a view to render. however i then got stuck in the whole respond method, what it was doing and the fact that the fields plugin assumes ( i think ) your rendering domain objects. In my case i wasnt - so sadly the use of the default generated views doesnt appear to work for me. I have tow options - do my own rendering /html work - was trying to avoid this. else cheat and take the remote objects and create cache copy domain objects locally and try and render these. Thats where i am – WILLIAM WOODMAN Oct 02 '18 at 15:25
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/181164/discussion-between-william-woodman-and-jeff-scott-brown). – WILLIAM WOODMAN Oct 02 '18 at 15:27
  • I wouldn't do either of those, but the answer to your question "Can i use GSP to render HTML for variables held in the model map where the data objects are not beans (such as my array of requests) and if so how do i do that ?" is "yes". I hope some of the above has been helpful. Best of luck! – Jeff Scott Brown Oct 02 '18 at 15:27