The code was in a Spring Boot app, so I created a Controller with a couple of URL mappings. The data being displayed is coming from an XML file, so there are some references to Groovy's XmlParser as well.
The renderHtml method creates the HTML, HEAD, and BODY tags and accepts additional page contents as a closure. In the example, there are two different pages that can be generated, both of which share the same style declarations because they both use renderHtml.
As I said, I don't think I'd do this in an actual application, but it is a way of creating an entire page's contents in a single file.
@RestController class TableController { @Autowired ManuscriptCatalog catalog @RequestMapping(value="/manuscripts/{manuscript}/tables") String displayTable(@PathVariable("manuscript") String manuscriptId) { def manuscript = new XmlParser().parse( new File(catalog.getManuscript(manuscriptId).@url)) def tableXmls = manuscript.depthFirst().findAll {it.name() == 'table'} return renderHtml { builder -> builder.h2(manuscript.properties[0].@caption.toString()) builder.ul { tableXmls.each { tableXml -> String tableName = tableXml.@id.toString() li { a(href: "/manuscripts/$manuscriptId/tables/$tableName",
tableName) } } } } } @RequestMapping(value="/manuscripts/{manuscript}/tables/{table}") String displayTable( @PathVariable("manuscript") String manuscriptId, @PathVariable("table") String tableName) { def manuscript = new XmlParser().parse( new File(catalog.getManuscript(manuscriptId).@url)) def tableXml = manuscript.depthFirst().find { it.name() == 'table' && it.attributes()['id'] == tableName} def table = Table.parseTableData(tableXml) return generateTableHtml(table) }
private String renderHtml(Closure pageContents) { StringWriter out = new StringWriter() def builder = new MarkupBuilder(out) builder.html { head { link(rel: 'stylesheet', href: 'http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css') style( '''\ .table-nonfluid { width: auto !important; } th, .key { background-color: #669900; font-weight: bold; color: white } tr:hover { background-color: #a6a6a6 !important; } ''') } builder.body { pageContents(builder) } } return out.toString() }
/** * Generate HTML based on the tables structure */ private String generateTableHtml(Table table) { return renderHtml { builder -> builder.h2(table.name) builder.table('class' : "table table-condensed table-bordered table-nonfluid") { thead { table.colKeys.eachWithIndex { key, index -> tr { if (index == table.colKeys.size() - 1) { table.rowKeys.collect { rowKey -> th(rowKey.name) } } else { th(colspan: table.rowKeys.size()) } key.repeated.times { key.values.each { keyValue -> th(colspan : key.span, keyValue) } } } } } tbody { table.rows.eachWithIndex { row, rowIndex -> tr { row.keys.eachWithIndex { key, keyIndex -> if (table.rowKeys[keyIndex].span == 1 || rowIndex % table.rowKeys[keyIndex].span == 0) { td('class' : 'key', rowspan : table.rowKeys[keyIndex].span, key) } } row.values.collect { val -> td(val) } } } } } } } }
Post a Comment