8.1.3 Models and Views - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith, Lari Hotari
Version: 3.1.1
8.1.3 Models and Views
Returning the Model
A model is a Map that the view uses when rendering. The keys within that Map correspond to variable names accessible by the view. There are a couple of ways to return a model. First, you can explicitly return a Map instance:def show() { [book: Book.get(params.id)] }
The above does not reflect what you should use with the scaffolding views - see the scaffolding section for more details.A more advanced approach is to return an instance of the Spring ModelAndView class:
import org.springframework.web.servlet.ModelAndViewdef index() { // get some books just for the index page, perhaps your favorites def favoriteBooks = ... // forward to the list view to show them return new ModelAndView("/book/list", [ bookList : favoriteBooks ]) }
attributes
application
Selecting the View
In both of the previous two examples there was no code that specified which view to render. So how does Grails know which one to pick? The answer lies in the conventions. Grails will look for a view at the locationgrails-app/views/book/show.gsp
for this show
action:class BookController { def show() { [book: Book.get(params.id)] } }
def show() {
def map = [book: Book.get(params.id)]
render(view: "display", model: map)
}
grails-app/views/book/display.gsp
. Notice that Grails automatically qualifies the view location with the book
directory of the grails-app/views
directory. This is convenient, but to access shared views you need instead you can use an absolute path instead of a relative one:def show() {
def map = [book: Book.get(params.id)]
render(view: "/shared/display", model: map)
}
grails-app/views/shared/display.gsp
.Grails also supports JSPs as views, so if a GSP isn't found in the expected location but a JSP is, it will be used instead.
Selecting Views For Namespaced Controllers
If a controller defines a namespace for itself with the namespace property that will affect the root directory in which Grails will look for views which are specified with a relative path. The default root directory for views rendered by a namespaced controller isgrails-app/views/<namespace name>/<controller name>/
. If the view is not found in the namespaced directory then Grails will fallback to looking for the view in the non-namespaced directory.See the example below.class ReportingController { static namespace = 'business' def humanResources() { // This will render grails-app/views/business/reporting/humanResources.gsp // if it exists. // If grails-app/views/business/reporting/humanResources.gsp does not // exist the fallback will be grails-app/views/reporting/humanResources.gsp. // The namespaced GSP will take precedence over the non-namespaced GSP. [numberOfEmployees: 9] } def accountsReceivable() { // This will render grails-app/views/business/reporting/accounting.gsp // if it exists. // If grails-app/views/business/reporting/accounting.gsp does not // exist the fallback will be grails-app/views/reporting/accounting.gsp. // The namespaced GSP will take precedence over the non-namespaced GSP. render view: 'numberCrunch', model: [numberOfEmployees: 13] } }
Rendering a Response
Sometimes it's easier (for example with Ajax applications) to render snippets of text or code to the response directly from the controller. For this, the highly flexiblerender
method can be used:render "Hello World!"
// write some markup
render {
for (b in books) {
div(id: b.id, b.title)
}
}
// render a specific view render(view: 'show')
// render a template for each item in a collection
render(template: 'book_template', collection: Book.list())
// render some text with encoding and content type render(text: "<xml>some xml</xml>", contentType: "text/xml", encoding: "UTF-8")
MarkupBuilder
to generate HTML for use with the render
method be careful of naming clashes between HTML elements and Grails tags, for example:import groovy.xml.MarkupBuilder … def login() { def writer = new StringWriter() def builder = new MarkupBuilder(writer) builder.html { head { title 'Log in' } body { h1 'Hello' form { } } } def html = writer.toString() render html }
MarkupBuilder
). To correctly output a <form>
element, use the following:def login() { // … body { h1 'Hello' builder.form { } } // … }