package asia.grails.test.scaff class Person { String firstName String lastName Date dateOfBirth static constraints = { firstName (blank: false) lastName (blank: false) dateOfBirth (blank: false) } }
class PersonController { static scaffold = true }You may also use a different name for the controller, by setting the scaffold property to the name of the data model:
class TestController { static scaffold = Person }With just these 3 lines of code, the following screens and their corresponding business logic are created at runtime:
List all records of the table with column sorting and pagination. Link to create a new record is provided.
A form where the user can create a new record. A link to the listing page is provided.
Show details of a table record. Fields are read only and can not be modified. But buttons to go to the edit page is provided. The user can also delete the record or go back to the listing page.
Edit form of a table record. The record can either be updated or deleted. A link back to the listing page is available.
You can generate the controller code, view code, or both. Before you can generate any code, run this console command on the project root.
grails compileController
Type the command below on the console to generate the controller code:
grails generate-controller asia.grails.test.scaff.Person
and the following code will be generated:
package asia.grails.test.scaff import org.springframework.dao.DataIntegrityViolationException class PersonController { static allowedMethods = [save: "POST", update: "POST", delete: "POST"] def index() { redirect(action: "list", params: params) } def list(Integer max) { params.max = Math.min(max ?: 10, 100) [personInstanceList: Person.list(params), personInstanceTotal: Person.count()] } def create() { [personInstance: new Person(params)] } def save() { def personInstance = new Person(params) if (!personInstance.save(flush: true)) { render(view: "create", model: [personInstance: personInstance]) return } flash.message = message(code: 'default.created.message', args: [message(code: 'person.label', default: 'Person'), personInstance.id]) redirect(action: "show", id: personInstance.id) } def show(Long id) { def personInstance = Person.get(id) if (!personInstance) { flash.message = message(code: 'default.not.found.message', args: [message(code: 'person.label', default: 'Person'), id]) redirect(action: "list") return } [personInstance: personInstance] } def edit(Long id) { def personInstance = Person.get(id) if (!personInstance) { flash.message = message(code: 'default.not.found.message', args: [message(code: 'person.label', default: 'Person'), id]) redirect(action: "list") return } [personInstance: personInstance] } def update(Long id, Long version) { def personInstance = Person.get(id) if (!personInstance) { flash.message = message(code: 'default.not.found.message', args: [message(code: 'person.label', default: 'Person'), id]) redirect(action: "list") return } if (version != null) { if (personInstance.version > version) { personInstance.errors.rejectValue("version", "default.optimistic.locking.failure", [message(code: 'person.label', default: 'Person')] as Object[], "Another user has updated this Person while you were editing") render(view: "edit", model: [personInstance: personInstance]) return } } personInstance.properties = params if (!personInstance.save(flush: true)) { render(view: "edit", model: [personInstance: personInstance]) return } flash.message = message(code: 'default.updated.message', args: [message(code: 'person.label', default: 'Person'), personInstance.id]) redirect(action: "show", id: personInstance.id) } def delete(Long id) { def personInstance = Person.get(id) if (!personInstance) { flash.message = message(code: 'default.not.found.message', args: [message(code: 'person.label', default: 'Person'), id]) redirect(action: "list") return } try { personInstance.delete(flush: true) flash.message = message(code: 'default.deleted.message', args: [message(code: 'person.label', default: 'Person'), id]) redirect(action: "list") } catch (DataIntegrityViolationException e) { flash.message = message(code: 'default.not.deleted.message', args: [message(code: 'person.label', default: 'Person'), id]) redirect(action: "show", id: id) } } }
Type the command below on the console to generate the view code:
grails generate-views asia.grails.test.scaff.Personand the following code will be generated:
list.gsp
<%@ page import="asia.grails.test.scaff.Person" %> <!DOCTYPE html> <html> <head> <meta name="layout" content="main"> <g:set var="entityName" value="${message(code: 'person.label', default: 'Person')}" /> <title><g:message code="default.list.label" args="[entityName]" /></title> </head> <body> <a href="#list-person" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content…"/></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-person" 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> <table> <thead> <tr> <g:sortableColumn property="firstName" title="${message(code: 'person.firstName.label', default: 'First Name')}" /> <g:sortableColumn property="lastName" title="${message(code: 'person.lastName.label', default: 'Last Name')}" /> <g:sortableColumn property="dateOfBirth" title="${message(code: 'person.dateOfBirth.label', default: 'Date Of Birth')}" /> </tr> </thead> <tbody> <g:each in="${personInstanceList}" status="i" var="personInstance"> <tr class="${(i % 2) == 0 ? 'even' : 'odd'}"> <td><g:link action="show" id="${personInstance.id}">${fieldValue(bean: personInstance, field: "firstName")}</g:link></td> <td>${fieldValue(bean: personInstance, field: "lastName")}</td> <td><g:formatDate date="${personInstance.dateOfBirth}" /></td> </tr> </g:each> </tbody> </table> <div class="pagination"> <g:paginate total="${personInstanceTotal}" /> </div> </div> </body> </html>
create.gsp
<%@ page import="asia.grails.test.scaff.Person" %> <!DOCTYPE html> <html> <head> <meta name="layout" content="main"> <g:set var="entityName" value="${message(code: 'person.label', default: 'Person')}" /> <title><g:message code="default.create.label" args="[entityName]" /></title> </head> <body> <a href="#create-person" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content…"/></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="list" action="list"><g:message code="default.list.label" args="[entityName]" /></g:link></li> </ul> </div> <div id="create-person" class="content scaffold-create" role="main"> <h1><g:message code="default.create.label" args="[entityName]" /></h1> <g:if test="${flash.message}"> <div class="message" role="status">${flash.message}</div> </g:if> <g:hasErrors bean="${personInstance}"> <ul class="errors" role="alert"> <g:eachError bean="${personInstance}" var="error"> <li <g:if test="${error in org.springframework.validation.FieldError}">data-field-id="${error.field}"</g:if>><g:message error="${error}"/></li> </g:eachError> </ul> </g:hasErrors> <g:form action="save" > <fieldset class="form"> <g:render template="form"/> </fieldset> <fieldset class="buttons"> <g:submitButton name="create" class="save" value="${message(code: 'default.button.create.label', default: 'Create')}" /> </fieldset> </g:form> </div> </body> </html>
edit.gsp
<%@ page import="asia.grails.test.scaff.Person" %> <!DOCTYPE html> <html> <head> <meta name="layout" content="main"> <g:set var="entityName" value="${message(code: 'person.label', default: 'Person')}" /> <title><g:message code="default.edit.label" args="[entityName]" /></title> </head> <body> <a href="#edit-person" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content…"/></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="list" action="list"><g:message code="default.list.label" args="[entityName]" /></g:link></li> <li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li> </ul> </div> <div id="edit-person" class="content scaffold-edit" role="main"> <h1><g:message code="default.edit.label" args="[entityName]" /></h1> <g:if test="${flash.message}"> <div class="message" role="status">${flash.message}</div> </g:if> <g:hasErrors bean="${personInstance}"> <ul class="errors" role="alert"> <g:eachError bean="${personInstance}" var="error"> <li <g:if test="${error in org.springframework.validation.FieldError}">data-field-id="${error.field}"</g:if>><g:message error="${error}"/></li> </g:eachError> </ul> </g:hasErrors> <g:form method="post" > <g:hiddenField name="id" value="${personInstance?.id}" /> <g:hiddenField name="version" value="${personInstance?.version}" /> <fieldset class="form"> <g:render template="form"/> </fieldset> <fieldset class="buttons"> <g:actionSubmit class="save" action="update" value="${message(code: 'default.button.update.label', default: 'Update')}" /> <g:actionSubmit class="delete" action="delete" value="${message(code: 'default.button.delete.label', default: 'Delete')}" formnovalidate="" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" /> </fieldset> </g:form> </div> </body> </html>
_form.gsp
<%@ page import="asia.grails.test.scaff.Person" %> <div class="fieldcontain ${hasErrors(bean: personInstance, field: 'firstName', 'error')} required"> <label for="firstName"> <g:message code="person.firstName.label" default="First Name" /> <span class="required-indicator">*</span> </label> <g:textField name="firstName" required="" value="${personInstance?.firstName}"/> </div> <div class="fieldcontain ${hasErrors(bean: personInstance, field: 'lastName', 'error')} required"> <label for="lastName"> <g:message code="person.lastName.label" default="Last Name" /> <span class="required-indicator">*</span> </label> <g:textField name="lastName" required="" value="${personInstance?.lastName}"/> </div> <div class="fieldcontain ${hasErrors(bean: personInstance, field: 'dateOfBirth', 'error')} required"> <label for="dateOfBirth"> <g:message code="person.dateOfBirth.label" default="Date Of Birth" /> <span class="required-indicator">*</span> </label> <g:datePicker name="dateOfBirth" precision="day" value="${personInstance?.dateOfBirth}" /> </div>
show.gsp
<%@ page import="asia.grails.test.scaff.Person" %> <!DOCTYPE html> <html> <head> <meta name="layout" content="main"> <g:set var="entityName" value="${message(code: 'person.label', default: 'Person')}" /> <title><g:message code="default.show.label" args="[entityName]" /></title> </head> <body> <a href="#show-person" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content…"/></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="list" action="list"><g:message code="default.list.label" args="[entityName]" /></g:link></li> <li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li> </ul> </div> <div id="show-person" class="content scaffold-show" role="main"> <h1><g:message code="default.show.label" args="[entityName]" /></h1> <g:if test="${flash.message}"> <div class="message" role="status">${flash.message}</div> </g:if> <ol class="property-list person"> <g:if test="${personInstance?.firstName}"> <li class="fieldcontain"> <span id="firstName-label" class="property-label"><g:message code="person.firstName.label" default="First Name" /></span> <span class="property-value" aria-labelledby="firstName-label"><g:fieldValue bean="${personInstance}" field="firstName"/></span> </li> </g:if> <g:if test="${personInstance?.lastName}"> <li class="fieldcontain"> <span id="lastName-label" class="property-label"><g:message code="person.lastName.label" default="Last Name" /></span> <span class="property-value" aria-labelledby="lastName-label"><g:fieldValue bean="${personInstance}" field="lastName"/></span> </li> </g:if> <g:if test="${personInstance?.dateOfBirth}"> <li class="fieldcontain"> <span id="dateOfBirth-label" class="property-label"><g:message code="person.dateOfBirth.label" default="Date Of Birth" /></span> <span class="property-value" aria-labelledby="dateOfBirth-label"><g:formatDate date="${personInstance?.dateOfBirth}" /></span> </li> </g:if> </ol> <g:form> <fieldset class="buttons"> <g:hiddenField name="id" value="${personInstance?.id}" /> <g:link class="edit" action="edit" id="${personInstance?.id}"><g:message code="default.button.edit.label" default="Edit" /></g:link> <g:actionSubmit class="delete" action="delete" value="${message(code: 'default.button.delete.label', default: 'Delete')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" /> </fieldset> </g:form> </div> </body> </html>All
Type the command below on the console to generate both the controller and view codes:
grails generate-all asia.grails.test.scaff.Person