Basic CRUD Web Aplikasi Menggunakan SpringFramework-MVC, Freemarker, Hibernate dan JPA - Spring Controller Class dan Freemarker View Templates

Article Index

Spring Controller Class dan Freemarker View Templates

Sebelum membuat class Controller, kita terlebih dahulu membuat class Message dan tempatkan pada package org.fajarapps.jpacrud.domain.

Source code Message.java:

package org.fajarapps.jpacrud.domain;

import java.io.Serializable;

/**
 * Message binding helper yang dipergunakan untuk menampilkan flash message pada halaman html.
 */
public final class Message implements Serializable
{
    private static final long serialVersionUID = 7503750389106359368L;
    public static final String ERROR = "error";
    public static final String SUCCESS = "success";
    private String message;
    private String type;

    public Message(String type, String message) {
        this.message = message;
        this.type = type;
    }

   /* getter dan setter ...  tidak ditampilkan */

    @Override
    public String toString() {
        return "Message{" +
                "message='" + message + '\'' +
                ", type='" + type + '\'' +
                '}';
    }
}

Class ini diperlukan untuk menampilkan pesan error ataupun success pada halaman web setelah operasi CRUD dilaksanakan.

Selanjutnya pada Project Explorer pilih package org.fajarapps.jpacrud dan buat package baru yakni controller. Pada package yang baru dibuat ini kita buat 3 class controller yakni: HomeController, DepartmentController dan EmployeeController.

Sedangkan semua file freemarker template kita tempatkan pada folder src/main/webapp/WEB-INF/freemarker dan memiliki extension .ftl.

Source code HomeController.java:

package org.fajarapps.jpacrud.controller;

/* import ... tidak ditampilkan */

/**
 * Controller untuk menampilkan halaman utama.
 */
@Controller
@RequestMapping(value = "/")
public class HomeController
{
    @RequestMapping(method = RequestMethod.GET)
    public String index(Model model) {
        model.addAttribute("pageTitle", "Welcome to JPA-Crud Project");

        return "index";
    }
}

Class HomeController dipergunakan untuk menampilkan halaman landing page (halaman utama).

Source code DepartmentController.java:

package org.fajarapps.jpacrud.controller;

/* import ... tidak ditampilkan */

/**
 * Controller untuk mengelola data Department.
 */
@Controller
@RequestMapping(value = "/department")
public class DepartmentController
{
    @Autowired
    private DepartmentModel departmentModel;
    @Autowired
    private MessageSource messageSource;


    @InitBinder
    public void initBinder(WebDataBinder binder) {
//        true treats empty string as NULL
        binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
    }

    // Menyimpan entity Department yang baru dibuat
    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public String createDepartment(@ModelAttribute("department") @Valid Department department, BindingResult result,
                                   Model model, RedirectAttributes redirectAttributes, Locale locale) {
        model.addAttribute("pageTitle", "New Department - JPA-Crud Project");

        if (result.hasErrors()) {
            model.addAttribute("messages", new Message(Message.ERROR,
                                                       messageSource.getMessage("validation.field.errors",
                                                                                new Object[]{}, locale)));
            return "department/form";
        }

        try {
            departmentModel.create(department);
            redirectAttributes.addFlashAttribute("messages", new Message(Message.SUCCESS, messageSource.getMessage(
                    "entity.department.CreateSuccess", new Object[]{}, locale)));
        } catch (Exception exc) {
            model.addAttribute("messages", new Message(Message.ERROR, exc.getMessage()));
            return "department/form";
        }

        return "redirect:/department";
    }

    // Menampilkan halaman form: menambah Department baru
    @RequestMapping(value = "/create", method = RequestMethod.GET)
    public String createDepartmentForm(Model model) {
        Department entity = new Department();
        model.addAttribute("department", entity);
        model.addAttribute("pageTitle", "New Department - JPA-Crud Project");

        return "department/form";
    }

    // Menghapus entity Department
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public String deleteDepartment(@RequestParam("dept") List<Integer> deptIds, RedirectAttributes redirectAttributes,
                                   Locale locale) {
        try {
            int num = departmentModel.delete(deptIds);
            redirectAttributes.addFlashAttribute("messages", new Message(Message.SUCCESS, messageSource.getMessage(
                    "entity.department.DeleteSuccess", new Object[]{num}, locale)));
        } catch (Exception exc) {
            redirectAttributes.addFlashAttribute("messages", new Message(Message.SUCCESS, messageSource.getMessage(
                    "entity.department.DeleteFailed", new Object[]{}, locale)));
        }

        return "redirect:/department";
    }

    // Menampilkan halaman daftar Department (halaman awal)
    @RequestMapping(method = RequestMethod.GET)
    public String displayAll(Model model) {
        DepartmentDataGrid dataGrid = new DepartmentDataGrid();
        Page result = departmentModel.listAll(
                new PageRequest(0, dataGrid.getPageSize(), Sort.Direction.ASC, "deptName"));
        dataGrid.setPageable(result).setSortDir("asc").setSortField("deptName");

        model.addAttribute("dataGrid", dataGrid);
        model.addAttribute("pages", result);
        model.addAttribute("pageTitle", "Department - JPA-Crud Project");

        return "department/list";
    }

    // Menampilkan halaman daftar Department (response dari action: next-page, last-page, previous-page, dan first-page)
    @RequestMapping(method = RequestMethod.POST)
    public String displayAllBinding(DepartmentDataGrid dataGrid, Model model) {
        int page = dataGrid.getPage() - 1;
        int pageSize = dataGrid.getPageSize();
        Page result = departmentModel.listAll(
                new PageRequest(page, pageSize, dataGrid.getSortSpec("deptName")));
        dataGrid.setPageable(result);
        model.addAttribute("dataGrid", dataGrid);
        model.addAttribute("pages", result);
        model.addAttribute("pageTitle", "Department - JPA-Crud Project");

        return "department/list";
    }

    // Menyimpan pembaruan entity Department
    @RequestMapping(value = "/edit", method = RequestMethod.POST)
    public String updateDepartment(@ModelAttribute("department") @Valid Department department, BindingResult result, Model model,
                                   RedirectAttributes redirectAttributes, Locale locale) {
        model.addAttribute("pageTitle", "Edit Department - JPA-Crud Project");

        if (result.hasErrors()) {
            model.addAttribute("messages", new Message(Message.ERROR,
                                                       messageSource.getMessage("validation.field.errors",
                                                                                new Object[]{}, locale)));
            return "department/form";
        }

        try {
            departmentModel.update(department);
            redirectAttributes.addFlashAttribute("messages", new Message(Message.SUCCESS, messageSource.getMessage(
                    "entity.department.UpdateSuccess", new Object[]{}, locale)));
        } catch (Exception exc) {
            model.addAttribute("messages", new Message(Message.ERROR, exc.getMessage()));
            return "department/form";
        }

        return "redirect:/department";
    }

    // Menampilkan halaman form: Sunting Department
    @RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
    public String updateDepartmentForm(@PathVariable("id") Integer id, Model model) {
        Department department = departmentModel.find(id);
        model.addAttribute("department", department);
        model.addAttribute("pageTitle", "Edit Department - JPA-Crud Project");

        return "department/form";
    }
}

Class DepartmentController dipergunakan untuk memproses halaman web yang berhubungan dengan menu Department.

Source code template src/main/webapp/WEB-INF/freemarker/department/form.ftl:

<#import "/spring.ftl" as spring />
<#assign xhtmlCompliant = true in spring>
<!doctype html>
<html>
<head>
<#include "../head-meta.ftl"/>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
  <div class="container">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
              aria-expanded="false" aria-controls="navbar">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="/<@spring.url "/"/>">JPA-Crud Project</a>
    </div>
    <div id="navbar" class="navbar-collapse collapse">
      <ul class="nav navbar-nav">
        <li><a href="/<@spring.url "/"/>">Home</a></li>
        <li><a href="/<@spring.url "/employee"/>">Employee</a></li>
        <li class="active"><a href="/<@spring.url "/department"/>">Department</a></li>
      </ul>
    </div>
  </div>
</nav>

<section class="container">
  <div class="page-header">
    <h1><#if (department.deptId)??>Sunting<#else>Tambah</#if> Departemen</h1>
  </div>
<#include "../messages.ftl"/>
  <form method="post" action="<#if (department.deptId)??><@spring.url "/department/edit"/>
  <#else><@spring.url "/department/create"/></#if>" id="deptForm" class="form-horizontal">
    <div class="form-group">
    <@spring.bind "department.deptName"/>
      <label for="${spring.status.expression}" class="control-label field-primary col-sm-3 col-md-2">Departemen</label>
      <div class="col-sm-9 col-md-7">
      <@spring.formInput "department.deptName", "class=\"form-control required\" minlength=\"3\""/>
      <#list spring.status.errorMessages as error><label class="error">${error}</label></#list>
      </div>
    </div>
    <div class="form-group">
    <@spring.bind "department.description"/>
      <label for="${spring.status.expression}" class="control-label col-sm-3 col-md-2">Keterangan</label>
      <div class="col-sm-9 col-md-10">
      <@spring.formInput "department.description", "class=\"form-control\""/>
      <#list spring.status.errorMessages as error><label class="error">${error}</label></#list>
      </div>
    </div>
    <@spring.formHiddenInput "department.deptId"/>
    <div class="form-group">
      <label class="control-label col-sm-3 col-md-2"></label>
      <div class="col-sm-9 col-md-10">
        <button type="submit" class="btn btn-primary">Simpan</button>
        <a class="btn btn-default" href="/<@spring.url "/department"/>" role="button">Batal</a>
      </div>
    </div>
  </form>
</section>
<#include "../footer.ftl"/>
<script type="text/javascript">
  $(document).ready(function () {
    var frm = $("#deptForm");
    $(".container").tooltip({selector: "[data-toggle=tooltip]", placement: "top", container: "body"});
    frm.validate({
      ignore: ""
    });
    frm.find(":input").first().focus();
  });
</script>
</body>
</html>

Template diatas dipergunakan untuk menampilkan halaman Tambah Departemen dan Sunting Departemen.

Source template src/main/webapp/WEB-INF/freemarker/department/list.ftl:

<#import "/spring.ftl" as spring />
<#assign xhtmlCompliant = true in spring>
<!doctype html>
<html>
<head>
<#include "../head-meta.ftl"/>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
  <div class="container">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
              aria-expanded="false" aria-controls="navbar">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="/<@spring.url "/"/>">JPA-Crud Project</a>
    </div>
    <div id="navbar" class="navbar-collapse collapse">
      <ul class="nav navbar-nav">
        <li><a href="/<@spring.url "/"/>">Home</a></li>
        <li><a href="/<@spring.url "/employee"/>">Employee</a></li>
        <li class="active"><a href="/<@spring.url "/department"/>">Department</a></li>
      </ul>
    </div>
  </div>
</nav>

<#assign caret>
<span class="pull-right glyphicon glyphicon-triangle-<#if dataGrid.sortDir == "asc">top<#else>bottom</#if>"></span>
</#assign>
<section class="container">
  <div class="page-header">
    <h1>Daftar Departemen</h1>
  </div>
<#include "../messages.ftl"/>
  <form method="post" action="<@spring.url "/department"/>" id="tableForm" class="form-horizontal">
    <article class="table-responsive">
      <table class="table htgrid">
        <thead>
        <tr>
          <th class="htgrid-cell-header text-right">#</th>
          <th class="htgrid-cell-header text-center"><label><input type="checkbox" id="toggle-check"/></label></th>
          <th class="htgrid-cell-header">
            <div class="cell-header-inner" title="Klik untuk mengurutkan" data-toggle="tooltip" rel="deptName">
            <#if dataGrid.sortField == "deptName">${caret}</#if>Departemen
            </div>
          </th>
          <th class="htgrid-cell-header hidden-xs">Keterangan</th>
          <th class="htgrid-cell-header">
            <div class="cell-header-inner" title="Klik untuk mengurutkan" data-toggle="tooltip"
                 rel="COUNT(p.personId)"><#if dataGrid.sortField == "COUNT(p.personId)">${caret}</#if># Employee
            </div>
          </th>
          <th class="htgrid-cell-header text-center hidden-xs">Action</th>
        </tr>
        </thead>
        <tbody>
        <#escape x as x?html>
            <#assign startNumber="${(dataGrid.page - 1) * dataGrid.pageSize}"/>
            <#list dataGrid.entries as item>
            <tr>
                <#assign offset="${(startNumber?number + item_index + 1)}">
              <td class="text-right">${offset}</td>
              <td class="text-center"><label>
                <input type="checkbox" id="cb${offset}" name="dept" value="${item.deptId}"/></label></td>
              <td>${item.deptName}</td>
              <td class="hidden-xs">${item.description}</td>
              <td>${item.numberOfPerson!}</td>
              <td class="text-center hidden-xs">
                <a class="btn btn-xs btn-default" href="/<@spring.url "/department/edit/${item.deptId}"/>"
                   title="Sunting" data-toggle="tooltip" role="button">
                  <span class="glyphicon glyphicon-edit"></span></a>
              </td>
            </tr>
            </#list>
        </#escape>
        </tbody>
      </table>
    </article>
  <#include "../tablegrid-footer.ftl"/>
  </form>
  <div class="form-group">
    <div class="pull-right">
      <a class="btn btn-default" href="/<@spring.url "/department/create"/>" title="Tambah departemen baru"
         data-toggle="tooltip" role="button"><span class="glyphicon glyphicon-plus"></span> New Department</a>
      <button type="button" id="delete-department" class="btn btn-danger" title="Hapus departemen"
              data-toggle="tooltip"><span class="glyphicon glyphicon-trash"></span> Delete Department</button>
    </div>
  </div>
</section>
<#include "../footer.ftl"/>
<script type="text/javascript">
  $(document).ready(function () {
    $(".container").tooltip({selector: "[data-toggle=tooltip]", placement: "top", container: "body"});
    $("#toggle-check").checkAll();
    $("#tableForm").HTGridAction(${dataGrid.totalPages});
    $("#delete-department").click(function () {
      var nchk = 0;
      $(":input[id^=cb]").each(function () {
        if (this.checked == true) {
          nchk++;
        }
      });
      if (nchk > 0) {
        $("#tableForm").attr("action", "<@spring.url "/department/delete"/>").submit();
      }
    });
  });
</script>
</body>
</html>

Template diatas dipergunakan untuk menampilkan halaman Daftar Departemen dalam bentuk dataGrid.

Source code EmployeeController.java:

package org.fajarapps.jpacrud.controller;

/* import ... tidak ditampilkan */

/**
 * Controller untuk mengelola daftar personil.
 */
@Controller
@RequestMapping(value = "/employee")
public class EmployeeController
{
    @Autowired
    private IDepartmentRepository departmentRepository;
    @Autowired
    private PersonModel personModel;
    @Autowired
    private MessageSource messageSource;

    @InitBinder
    public void initBinder(WebDataBinder binder) {
//        true treats empty string as NULL
        binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
    }

    // Menyimpan entity Person baru
    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public String createPerson(@ModelAttribute("person") @Valid Person person, BindingResult result, Model model,
                               RedirectAttributes redirectAttributes, Locale locale) {
        model.addAttribute("pageTitle", "New Employee - JPA-Crud Project");

        if (result.hasErrors()) {
            model.addAttribute("messages", new Message(Message.ERROR,
                                                       messageSource.getMessage("validation.field.errors",
                                                                                new Object[]{}, locale)));
            return "employee/form";
        }

        try {
            personModel.create(person);
            redirectAttributes.addFlashAttribute("messages", new Message(Message.SUCCESS, messageSource.getMessage(
                    "entity.person.CreateSuccess", new Object[]{}, locale)));
        } catch (Exception exc) {
            model.addAttribute("messages", new Message(Message.ERROR, exc.getMessage()));
            return "employee/form";
        }

        return "redirect:/employee";
    }

    // Menampilkan halaman form: Tambah Employee
    @RequestMapping(value = "/create", method = RequestMethod.GET)
    public String createPersonForm(Model model) {
        Person person = new Person();
        model.addAttribute("person", person);
        model.addAttribute("gender", person);
        model.addAttribute("pageTitle", "New Employee - JPA-Crud Project");

        return "employee/form";
    }

    // Menghapus entity Person
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public String deletePerson(@RequestParam("pid") List<Long> personIds, RedirectAttributes redirectAttributes,
                               Locale locale) {
        try {
            int num = personModel.delete(personIds);
            redirectAttributes.addFlashAttribute("messages", new Message(Message.SUCCESS, messageSource.getMessage(
                    "entity.person.DeleteSuccess", new Object[]{num}, locale)));
        } catch (Exception exc) {
            redirectAttributes.addFlashAttribute("messages", new Message(Message.SUCCESS, messageSource.getMessage(
                    "entity.person.DeleteFailed", new Object[]{}, locale)));
        }

        return "redirect:/employee";
    }

    // Menampilkan halaman Daftar Employee (halaman awal)
    @RequestMapping(method = RequestMethod.GET)
    public String displayAll(Model model) {
        EmployeeDataGrid dataGrid = new EmployeeDataGrid();
        Page result = personModel.listAll(
                new PageRequest(0, dataGrid.getPageSize(), Sort.Direction.ASC, "fullname"));
        dataGrid.setPageable(result).setSortDir("asc").setSortField("fullname");

        model.addAttribute("dataGrid", dataGrid);
        model.addAttribute("pages", result);
        model.addAttribute("pageTitle", "Employee - JPA-Crud Project");

        return "employee/list";
    }

    // Menampilkan halaman Daftar Employee (response dari action: next-page, last-page, previous-page dan first-page)
    @RequestMapping(method = RequestMethod.POST)
    public String displayAllBinding(EmployeeDataGrid employeeGrid, Model model) {
        int page = employeeGrid.getPage() - 1;
        int pageSize = employeeGrid.getPageSize();

        PageRequest paging = new PageRequest(page, pageSize, employeeGrid.getSortSpec("fullname"));
        Page result = personModel.findAllByCriteria(employeeGrid.getDepartmentId(), employeeGrid.getTerm(),
                                                            paging);
        employeeGrid.setPageable(result);
        model.addAttribute("dataGrid", employeeGrid);
        model.addAttribute("pages", result);
        model.addAttribute("pageTitle", "Employee - JPA-Crud Project");

        return "employee/list";
    }

    @ModelAttribute(value = "departments")
    public Iterable<Department> listDepartments() {
        return departmentRepository.findAll(new Sort(Sort.Direction.ASC, "deptName"));
    }

    // Menyimpan pembaruan entity Person
    @RequestMapping(value = "/edit", method = RequestMethod.POST)
    public String updatePerson(@ModelAttribute("person") @Valid Person person, BindingResult result, Model model,
                               RedirectAttributes redirectAttributes, Locale locale) {
        model.addAttribute("pageTitle", "Edit Employee - JPA-Crud Project");

        if (result.hasErrors()) {
            model.addAttribute("messages", new Message(Message.ERROR,
                                                       messageSource.getMessage("validation.field.errors",
                                                                                new Object[]{}, locale)));
            return "employee/form";
        }

        try {
            personModel.update(person);
            redirectAttributes.addFlashAttribute("messages", new Message(Message.SUCCESS, messageSource.getMessage(
                    "entity.person.UpdateSuccess", new Object[]{}, locale)));
        } catch (Exception exc) {
            model.addAttribute("messages", new Message(Message.ERROR, exc.getMessage()));
            return "employee/form";
        }

        return "redirect:/employee";
    }

    // Menampilkan halaman form: Sunting Employee
    @RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
    public String updatePersonForm(@PathVariable("id") Long id, Model model) {
        Person person = personModel.find(id);
        model.addAttribute("person", person);
        model.addAttribute("pageTitle", "Edit Employe - JPA-Crud Project");

        return "employee/form";
    }
}

Class EmployeeController dipergunakan untuk memproses halaman web yang berhubungan dengan menu Employee.

Source code template src/main/webapp/WEB-INF/freemarker/employee/form.ftl:

<#import "/spring.ftl" as spring />
<#assign xhtmlCompliant = true in spring>
<!doctype html>
<html>
<head>
<#include "../head-meta.ftl"/>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
  <div class="container">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
              aria-expanded="false" aria-controls="navbar">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="/<@spring.url "/"/>">JPA-Crud Project</a>
    </div>
    <div id="navbar" class="navbar-collapse collapse">
      <ul class="nav navbar-nav">
        <li><a href="/<@spring.url "/"/>">Home</a></li>
        <li class="active"><a href="/<@spring.url "/employee"/>">Employee</a></li>
        <li><a href="/<@spring.url "/department"/>">Department</a></li>
      </ul>
    </div>
  </div>
</nav>

<div class="container">
  <div class="page-header">
    <h1><#if (person.personId)??>Sunting<#else>Tambah</#if> Employee</h1>
  </div>
    <#include "../messages.ftl"/>
  <form method="post" action="<#if (person.personId)??><@spring.url "/employee/edit"/>
  <#else><@spring.url "/employee/create"/></#if>" id="personForm" class="form-horizontal">
    <div class="form-group">
        <@spring.bind "person.fullname"/>
      <label for="${spring.status.expression}" class="control-label field-primary col-sm-3 col-md-2">Nama
        Lengkap</label>
      <div class="col-sm-9 col-md-7">
          <@spring.formInput "person.fullname", "class=\"form-control required\" minlength=\"5\""/>
          <#list spring.status.errorMessages as error><label class="error">${error}</label></#list>
      </div>
    </div>
    <div class="form-group">
        <@spring.bind "person.department"/>
      <label for="departments" class="control-label field-primary col-sm-3 col-md-2">Departemen</label>
      <div class="col-sm-9 col-md-5">
        <select name="${spring.status.expression}.deptId" class="chosen-select required"
                data-placeholder="-- Pilih Departemen --">
          <option value=""></option>
            <#list departments as dept>
              <option value="${dept.deptId}" <#if (person.department.deptId)?? && dept.deptId == person.department.deptId>
                      selected="selected"</#if> >${dept.deptName}</option>
            </#list>
        </select>
          <#list spring.status.errorMessages as error><label class="error">${error}</label></#list>
      </div>
    </div>
    <div class="form-group">
        <@spring.bind "person.address"/>
      <label for="${spring.status.expression}" class="control-label field-primary col-sm-3 col-md-2">Alamat</label>
      <div class="col-sm-9 col-md-7">
          <@spring.formTextarea "person.address", "class=\"form-control required\""/>
          <#list spring.status.errorMessages as error><label class="error">${error}</label></#list>
      </div>
    </div>
    <div class="form-group">
        <@spring.bind "person.province"/>
      <label for="${spring.status.expression}" class="control-label field-primary col-sm-3 col-md-2">Propinsi</label>
      <div class="col-sm-9 col-md-7">
          <@spring.formInput "person.province", "class=\"form-control required\""/>
          <#list spring.status.errorMessages as error><label class="error">${error}</label></#list>
      </div>
    </div>
    <div class="form-group">
        <@spring.bind "person.homePhone"/>
      <label for="${spring.status.expression}" class="control-label col-sm-3 col-md-2">Telpon Rumah</label>
      <div class="col-sm-5 col-md-3">
          <@spring.formInput "person.homePhone", "class=\"form-control\""/>
          <#list spring.status.errorMessages as error><label class="error">${error}</label></#list>
      </div>
    </div>
    <div class="form-group">
        <@spring.bind "person.workPhone"/>
      <label for="${spring.status.expression}" class="control-label col-sm-3 col-md-2">Telpon Kantor</label>
      <div class="col-sm-5 col-md-3">
          <@spring.formInput "person.workPhone", "class=\"form-control\""/>
          <#list spring.status.errorMessages as error><label class="error">${error}</label></#list>
      </div>
    </div>
    <div class="form-group">
        <@spring.bind "person.mobilePhone"/>
      <label for="${spring.status.expression}" class="control-label col-sm-3 col-md-2">No. Handphone</label>
      <div class="col-sm-5 col-md-3">
          <@spring.formInput "person.mobilePhone", "class=\"form-control\""/>
          <#list spring.status.errorMessages as error><label class="error">${error}</label></#list>
      </div>
    </div>
    <div class="form-group">
        <@spring.bind "person.email"/>
      <label for="${spring.status.expression}" class="control-label col-sm-3 col-md-2">Email</label>
      <div class="col-sm-5 col-md-3">
          <@spring.formInput "person.email", "class=\"form-control email\""/>
          <#list spring.status.errorMessages as error><label class="error">${error}</label></#list>
      </div>
    </div>
    <div class="form-group">
        <@spring.bind "person.gender"/>
      <label for="${spring.status.expression}" class="control-label field-primary col-sm-3 col-md-2">Jenis Kelamin</label>
      <div class="col-sm-7 col-md-6">
        <div class="btn-group" data-toggle="buttons">
          <label class="btn btn-primary <#if (person.gender)?? && person.gender == "L">active</#if>">
            <input type="radio" name="${spring.status.expression}" id="option1" autocomplete="off" value="L"
                   <#if (person.gender)?? && person.gender == "L">checked</#if>>Laki-Laki
          </label>
          <label class="btn btn-primary <#if (person.gender)?? && person.gender == "P">active</#if>">
            <input type="radio" name="${spring.status.expression}" id="option2" autocomplete="off" value="P"
                   <#if (person.gender)?? && person.gender == "P">checked</#if>>Perempuan
          </label>
        </div>
          <#list spring.status.errorMessages as error><label class="error">${error}</label></#list>
      </div>
    </div>
    <div class="form-group">
        <@spring.bind "person.birthPlace"/>
      <label for="${spring.status.expression}" class="control-label col-sm-3 col-md-2">Tempat Lahir</label>
      <div class="col-sm-9 col-md-7">
          <@spring.formInput "person.birthPlace", "class=\"form-control\""/>
          <#list spring.status.errorMessages as error><label class="error">${error}</label></#list>
      </div>
    </div>
    <div class="form-group">
        <@spring.bind "person.birthDate"/>
      <label for="${spring.status.expression}" class="control-label field-primary col-sm-3 col-md-2">Tanggal Lahir</label>
      <div class="col-sm-5 col-md-3">
          <@spring.formInput "person.birthDate", "class=\"form-control\" placeholder=\"format: dd-mm-yyyy\""/>
          <#list spring.status.errorMessages as error><label class="error">${error}</label></#list>
      </div>
    </div>
      <@spring.formHiddenInput "person.personId"/>
    <div class="form-group">
      <label class="control-label col-sm-3 col-md-2"></label>
      <div class="col-sm-9 col-md-10">
        <button type="submit" class="btn btn-primary">Simpan</button>
        <a class="btn btn-default" href="/<@spring.url "/employee"/>" role="button">Batal</a>
      </div>
    </div>
  </form>
</div>
<#include "../footer.ftl"/>
<script type="text/javascript">
  $(document).ready(function () {
    var frm = $("#personForm");
    $(".container").tooltip({selector: "[data-toggle=tooltip]", placement: "top", container: "body"});
    $(".chosen-select").chosen({ allow_single_deselect: true });
    frm.validate({
      ignore: "",
      rules : {email: true}
    });
    frm.find(":input").first().focus();
  });
</script>
</body>
</html>

Template diatas dipergunakan untuk menampilkan halaman form: Tambah Employee dan Sunting Employee.

Source code template src/main/webapp/WEB-INF/freemarker/employee/list.ftl:

<#import "/spring.ftl" as spring />
<#assign xhtmlCompliant = true in spring>
<!doctype html>
<html>
<head>
<#include "../head-meta.ftl"/>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
  <div class="container">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
              aria-expanded="false" aria-controls="navbar">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="/<@spring.url "/"/>">JPA-Crud Project</a>
    </div>
    <div id="navbar" class="navbar-collapse collapse">
      <ul class="nav navbar-nav">
        <li><a href="/<@spring.url "/"/>">Home</a></li>
        <li class="active"><a href="/<@spring.url "/employee"/>">Employee</a></li>
        <li><a href="/<@spring.url "/department"/>">Department</a></li>
      </ul>
    </div>
  </div>
</nav>

<#assign caret>
<span class="pull-right glyphicon glyphicon-triangle-<#if dataGrid.sortDir == "asc">top<#else>bottom</#if>"></span>
</#assign>
<section class="container">
  <div class="page-header">
    <h1>Daftar Employee</h1>
  </div>
<#include "../messages.ftl"/>
  <form method="post" action="<@spring.url "/employee"/>" id="tableForm" class="form-horizontal">
    <div class="clearfix" style="margin-bottom: 20px">
      <div class="col-xs-5 col-sm-4 col-md-3 col-lg-2 pull-right">
        <div class="row">
          <select id="departments" name="departmentId" class="chosen-select" data-placeholder="-- Pilih Departemen --">
            <option value=""></option>
          <#list departments as dept>
            <option value="${dept.deptId}"
                    <#if (dataGrid.departmentId)?? && dataGrid.departmentId == dept.deptId>selected</#if>>${dept.deptName}</option>
          </#list>
          </select>
        </div>
      </div>
      <div class="col-xs-6 col-sm-4">
        <div class="row">
          <div class="input-group input-group">
          <@spring.formInput "dataGrid.term", "class=\"form-control\" data-toggle=\"tooltip\" placeholder=\"Ketik nama employee\""/>
            <span class="input-group-btn">
                  <button type="submit" class="btn btn-default"><i class="glyphicon glyphicon-search"></i>
                  </button>
              </span>
          </div>
        </div>
      </div>
    </div>
    <article class="table-responsive">
      <table class="table htgrid">
        <thead>
        <tr>
          <th class="htgrid-cell-header text-right">#</th>
          <th class="htgrid-cell-header text-center"><label><input type="checkbox" id="toggle-check"/></label></th>
          <th class="htgrid-cell-header">
            <div class="cell-header-inner text-nowrap" title="Klik untuk mengurutkan" data-toggle="tooltip"
                 rel="fullname">
            <#if dataGrid.sortField == "fullname">${caret}</#if>Nama Lengkap
            </div>
          </th>
          <th class="htgrid-cell-header">
            <div class="cell-header-inner" title="Klik untuk mengurutkan" data-toggle="tooltip"
                 rel="department.deptName"><#if dataGrid.sortField == "department.deptName">${caret}</#if>Departemen
            </div>
          </th>
          <th class="htgrid-cell-header hidden-xs hidden-sm hidden-md">Alamat</th>
          <th class="htgrid-cell-header">
            <div class="cell-header-inner" title="Klik untuk mengurutkan" data-toggle="tooltip" rel="province">
            <#if dataGrid.sortField == "province">${caret}</#if>Propinsi
            </div>
          </th>
          <th class="htgrid-cell-header hidden-xs">
            <div class="cell-header-inner" title="Klik untuk mengurutkan" data-toggle="tooltip" rel="birthPlace">
            <#if dataGrid.sortField == "birthPlace">${caret}</#if>Tempat Lahir
            </div>
          </th>
          <th class="htgrid-cell-header hidden-xs hidden-sm">
            <div class="cell-header-inner" title="Klik untuk mengurutkan" data-toggle="tooltip" rel="birthDate">
            <#if dataGrid.sortField == "birthDate">${caret}</#if>Tgl. Lahir
            </div>
          </th>
          <th class="htgrid-cell-header hidden-xs hidden-sm">
            <div class="cell-header-inner" title="Klik untuk mengurutkan" data-toggle="tooltip" rel="tsCreated">
            <#if dataGrid.sortField == "tsCreated">${caret}</#if>Tgl. Terdaftar
            </div>
          </th>
          <th class="htgrid-cell-header hidden-xs hidden-sm">Action</th>
        </tr>
        </thead>
        <tbody>
        <#escape x as x?html>
            <#assign startNumber="${(dataGrid.page - 1) * dataGrid.pageSize}"/>
            <#list dataGrid.entries as person>
            <tr>
                <#assign offset="${startNumber?number + person_index + 1}">
              <td class="text-right">${offset}</td>
              <td class="text-center"><label>
                <input type="checkbox" id="cb${offset}" name="pid" value="${person.personId}"/></label></td>
              <td class="text-nowrap">${person.fullname}</td>
              <td>${person.department.deptName}</td>
              <td class="hidden-xs hidden-sm hidden-md">${person.address}</td>
              <td>${person.province}</td>
              <td class="hidden-xs">${person.birthPlace}</td>
              <td class="hidden-xs hidden-sm">${person.birthDate?string("dd-MM-yyyy")}</td>
              <td class="text-nowrap">${person.tsCreated?string("dd-MM-yyyy HH:mm:ss")}</td>
              <td class="text-center hidden-xs hidden-sm">
                <a class="btn btn-xs btn-default" href="/<@spring.url "/employee/edit/${person.personId}"/>"
                   title="Sunting" data-toggle="tooltip" role="button">
                  <span class="glyphicon glyphicon-edit"></span></a>
              </td>
            </tr>
            </#list>
        </#escape>
        </tbody>
      </table>
    </article>
  <#include "../tablegrid-footer.ftl"/>
  </form>
  <div class="form-group">
    <div class="pull-right">
      <a class="btn btn-default" href="/<@spring.url "/employee/create"/>" role="button">
        <span class="glyphicon glyphicon-plus"></span> New Employee</a>
      <button type="button" id="delete-employee" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span>
        Delete Employee
      </button>
    </div>
  </div>
</section>
<#include "../footer.ftl"/>
<script type="text/javascript">
  $(document).ready(function () {
    $(".container").tooltip({selector: "[data-toggle=tooltip]", placement: "top", container: "body"});
    $("#toggle-check").checkAll();
    $(".chosen-select").chosen({ allow_single_deselect: true });
    $("#tableForm").HTGridAction(${dataGrid.totalPages});
    $("#delete-employee").click(function () {
      var nchk = 0;
      $(":input[id^=cb]").each(function () {
        if (this.checked == true) {
          nchk++;
        }
      });
      if (nchk > 0) {
        $("#tableForm").attr("action", "<@spring.url "/employee/delete"/>").submit();
      }
    });
  });
</script>
</body>
</html>

Template diatas dipergunakan untuk menampilkan halaman Daftar Employee dalam bentuk dataGrid.