JDAL Spring MVC and YUI DataTable Sample
To use JDAL in web enviroment with Spring MVC and YUI DataTable write three classes and one JSP page:
- A Spring MVC Controller: i.e. BookController
- A Dto. for Books, that is because we need to serialize Books as json strings.
- A ModelMapper to convert Books to BooksDtos. (ie, an Assembler)
- A JSP with filter form and YUI table configuration.
We reuse the integration and service layers of the sample book library that includes the three models (Book, Author and Category) and two of the persistent services (BookService and CategoryService).
Dto and ModelMapper
BookDto:
/* * Copyright 2009-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jdal.samples.mvc; import java.util.Date; import org.jdal.samples.model.Book; /** * Dto to serialize as json String * * @author Jose Luis Martin */ public class BookDto { private long id; private String author; private String category; private String isbn; private long publishedDate; private String name; public BookDto() { } /** * @param b */ public BookDto(Book b) { this.id = b.getId(); this.author = b.getAuthor().toString(); this.category = b.getCategory().toString(); this.isbn = b.getIsbn(); this.publishedDate = b.getPublishedDate().getTime(); this.name = b.getName(); } /** * @return the id */ public long getId() { return id; } /** * @param id the id to set */ public void setId(long id) { this.id = id; } /** * @return the author */ public String getAuthor() { return author; } /** * @param author the author to set */ public void setAuthor(String author) { this.author = author; } /** * @return the category */ public String getCategory() { return category; } /** * @param category the category to set */ public void setCategory(String category) { this.category = category; } /** * @return the isbn */ public String getIsbn() { return isbn; } /** * @param isbn the isbn to set */ public void setIsbn(String isbn) { this.isbn = isbn; } /** * @return the publishedDate */ public long getPublishedDate() { return publishedDate; } /** * @param publishedDate the publishedDate to set */ public void setPublishedDate(long publishedDate) { this.publishedDate = publishedDate; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } }
Book ModelMapper:
/* * Copyright 2009-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jdal.samples.mvc; import org.jdal.samples.model.Book; import org.jdal.web.table.ModelMapperSupport; /** * Model Mapper for Books * * @author Jose Luis Martin */ public class BookModelMapper extends ModelMapperSupport { /** * {@inheritDoc} */ @Override public Object fromModel(Object obj) { return new BookDto((Book) obj); } /** * {@inheritDoc} */ @Override public Object toModel(Object obj) { return null; } }
Controller
In BookController, store the filter on the session object using the Spring MVC @SessionAtributtes annotation. Bind the list of categories to request with Spring MVC @ModelAttributes for showing it on the filter form. In getData() method we query for page and write response as JSon string to send data to YUI DataTable.
/* * Copyright 2009-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jdal.samples.mvc; import info.joseluismartin.dao.Filter; import info.joseluismartin.dao.Page; import info.joseluismartin.service.PersistentService; import java.beans.PropertyEditorSupport; import java.io.IOException; import java.util.List; import javax.servlet.http.HttpServletResponse; import net.sf.json.JSON; import net.sf.json.JSONSerializer; import org.jdal.samples.dao.filter.BookFilter; import org.jdal.samples.model.Category; import org.jdal.web.table.DataTableModel; import org.jdal.web.table.ModelMapper; import org.springframework.stereotype.Controller; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.SessionAttributes; /** * Controller to handle "/book" requests * * @author Jose Luis Martin */ @Controller @SessionAttributes("filter") // Store filter on Session public class BookController { /** Persistent service for book categories */ PersistentService<Category, Long> categoryService; /** Persistent Service for Books */ PersistentService<Object, Long> bookService; /** Model mapper */ ModelMapper modelMapper; /** * Handle getPage request. Gets the Page from PaginatedListAdapter wrapper * and query service for data. * @param paginatedList the displaytag PaginatedList interface * @param filter filter to apply * @return Model */ @RequestMapping void getPage(@ModelAttribute Filter filter) { // do nothing, only store filter on session // the table will request data using AJAX } @RequestMapping public void getData(HttpServletResponse response, @ModelAttribute DataTableModel dtm, @ModelAttribute Filter filter) throws IOException { Page<Object> page = dtm.buildPage(); page.setFilter(filter); bookService.getPage(page); dtm.setRecords(modelMapper.fromModel(page.getData())); dtm.setTotalRecords(page.getCount()); JSON json = JSONSerializer.toJSON(dtm); // write json string to response response.addHeader("Pragma", "no-cache"); response.addHeader("Cache-Control", "no-cache"); // HTTP/1.1 response.addHeader("Cache-Control", "no-store"); // HTTP/1.1 response.addHeader("Cache-Control", "must-revalidate"); // HTTP/1.1 response.setContentType("application/x-json;charset=UTF-8"); json.write(response.getWriter()); } /** * Add the category list to model * @return List with all categories */ @ModelAttribute("categoryList") public List<Category> getCategories() { return categoryService.getAll(); } /** * Need to register the CategoryPropertyEditor for mapping ids of * category select to Category objects in bd. * @param binder */ @InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Category.class, new CategoryPropertyEditor()); } @ModelAttribute public Filter getFilter() { return new BookFilter(); } /** * @return the categoryService */ public PersistentService<Category, Long> getCategoryService() { return categoryService; } /** * @param categoryService the categoryService to set */ public void setCategoryService(PersistentService<Category, Long> categoryService) { this.categoryService = categoryService; } /** * @return the bookService */ public PersistentService<Object, Long> getBookService() { return bookService; } /** * @param bookService the bookService to set */ public void setBookService(PersistentService<Object, Long> bookService) { this.bookService = bookService; } /** * Property editor to map selected category from id string to Category. */ class CategoryPropertyEditor extends PropertyEditorSupport { /** * {@inheritDoc} */ @Override public void setAsText(String text) throws IllegalArgumentException { Long id = Long.parseLong(text); Category value = id == 0 ? null : categoryService.get(id); setValue(value); } /** * {@inheritDoc} */ @Override public String getAsText() { Category c = (Category) getValue(); return c != null ? String.valueOf(c.getId()) : ""; } } /** * @return the modelMapper */ public ModelMapper getModelMapper() { return modelMapper; } /** * @param modelMapper the modelMapper to set */ public void setModelMapper(ModelMapper modelMapper) { this.modelMapper = modelMapper; } }
View
Finally, write the JSP page:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <!-- Core + Skin CSS --> <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.9.0/build/paginator/assets/skins/sam/paginator.css"/> <link rel="stylesheet" type="text/css" href="<c:out value="${pageContext.request.contextPath}"/>/styles/displaytag.css"/> <title>JDAL Spring MVC and YUI DataTable Sample</title> <!--CSS file (default YUI Sam Skin) --> <link type="text/css" rel="stylesheet" href="http://yui.yahooapis.com/2.9.0/build/datatable/assets/skins/sam/datatable.css"> <!-- Dependencies --> <script src="http://yui.yahooapis.com/2.9.0/build/yahoo-dom-event/yahoo-dom-event.js"></script> <script src="http://yui.yahooapis.com/2.9.0/build/element/element-min.js"></script> <script src="http://yui.yahooapis.com/2.9.0/build/datasource/datasource-min.js"></script> <!-- OPTIONAL: JSON Utility (for DataSource) --> <script src="http://yui.yahooapis.com/2.9.0/build/json/json-min.js"></script> <!-- OPTIONAL: Connection Manager (enables XHR for DataSource) --> <script src="http://yui.yahooapis.com/2.9.0/build/connection/connection-min.js"></script> <!-- OPTIONAL: Get Utility (enables dynamic script nodes for DataSource) --> <script src="http://yui.yahooapis.com/2.9.0/build/get/get-min.js"></script> <!-- OPTIONAL: Drag Drop (enables resizeable or reorderable columns) --> <script src="http://yui.yahooapis.com/2.9.0/build/dragdrop/dragdrop-min.js"></script> <!-- OPTIONAL: Calendar (enables calendar editors) --> <script src="http://yui.yahooapis.com/2.9.0/build/calendar/calendar-min.js"></script> <!-- Source files --> <script src="http://yui.yahooapis.com/2.9.0/build/datatable/datatable-min.js"></script> <script src="http://yui.yahooapis.com/2.9.0/build/paginator/paginator-min.js"></script> <script src="<c:out value="${pageContext.request.contextPath}"/>/scripts/jdal.js"></script> </head> <body class="yui-skin-sam"> <h1>JDAL Spring MVC and YUI DataTable Sample</h1> <form:form commandName="filter" action="getPage" method="POST"> <table> <tr> <td>Book Title:</td> <td><form:input path="name" /></td> </tr> <tr> <td>Category:</td> <td><form:select path="category"> <form:option value="0">--Please Select</form:option> <form:options items="${categoryList}" itemValue="id" itemLabel="name" />< </form:select> </td> </tr> <tr> <td>ISBN:</td> <td><form:input path="isbn" /></td> </tr> <tr> <td>Author Name:</td> <td><form:input path="authorName" /></td> </tr> <tr> <td>Author Surname:</td> <td><form:input path="authorSurname" /></td> </tr> <tr> <td colspan="2"> <input type="submit" value="Apply Filter" /> </td> </tr> </table> </form:form> <br> <div id="bookTable"></div> <script> // Defer instantiation of dataTable until page is loaded YAHOO.util.Event.addListener(window, "load", function() { var bookTable = new YAHOO.widget.DataTable("bookTable", cols, dataSource, { generateRequest : JDAL.requestBuilder, initialRequest : JDAL.requestBuilder(), dynamicData : true, // Enables dynamic server-driven data sortedBy : { key : "id", dir : YAHOO.widget.DataTable.CLASS_ASC }, // Sets UI initial sort arrow paginator : new YAHOO.widget.Paginator( { rowsPerPage : 10 }) // Enables pagination }); // update totalRecords and startIndex bookTable.doBeforeLoadData = function(oRequest, oResponse, oPayload) { oPayload.totalRecords = oResponse.meta.totalRecords; oPayload.pagination.recordOffset = oResponse.meta.startIndex; return oPayload; }; }); // The label is the text that will be rendered in the table head var cols = [ { key: "id", label: "ID", sortable: "true", width: 50 }, { key: "name", label: "Title", sortable: "true", width: 250 }, { key: "category", label: "Category", sortable: "true", width: 140 }, { key: "author", label: "Author", sortable: "true", width: 180 }, { key: "publishedDate", label: "Published Date", sortable: "true", formatter:"date", width: 120 }, { key: "isbn", label: "ISBN", sortable: "true", width: 180 } ]; // Custom Date parser var timestampToDate = function(oData) { return new Date(oData); }; // DataSource instance var dataSource = new YAHOO.util.DataSource("getData?"); dataSource.responseType = YAHOO.util.DataSource.TYPE_JSON; dataSource.responseSchema = { resultsList: "records", fields: [ {key:"id", parser:"number"}, {key:"name"}, {key:"publishedDate", parser:timestampToDate}, {key:"category"}, {key:"author"}, {key:"isbn"} ], // Access to values in the server response metaFields: { totalRecords: "totalRecords", startIndex: "startIndex" } }; </script> </body> </html>
Sample Source Code
Source code is in jdal-samples.tgz file. Download the latest version from sourceforge.
If you have comments, questions or something else, please make a post on project support forums. I will try to answer it as soon as I can.