Integrate ZK with Spring MVC 3

From Documentation
DocumentationSmall Talks2012NovemberIntegrate ZK with Spring MVC 3
Integrate ZK with Spring MVC 3

Author
Vincent Jian, Engineer, Potix Corporation
Date
November 29, 2012
Version
ZK 6.0.2/6.5.0 , Spring MVC 3.1.2

Introduction

Spring MVC is a request-based Model-View-Controller web framework. It is easy to define page flow with Spring MVC. However, when the website gets too complex, it becomes harder to maintain the page flows. Therefore, it's a good idea to implement some part of functions by component-based (ajax-based) framework like ZK framework to reduce the page flows. This article will demonstrate how to communicate between Spring MVC to ZK MVVM with a simple shopping cart sample.

Prerequisites

In this article, we assume you are familiar with Spring MVC and ZK MVVM already. Refer to the following links if you are not familiar:

Page flow of sample project[1]

The typical shopping cart page flow is shown below.

Zkspringmvc-flow1.png

Here we can replace the loop part (buy one product -> view cart -> buy another product -> view cart) with ZK to simplify the process as shown below.

Zkspringmvc-flow2.png

Notes

  1. This article focuses on the communication mechanism between ZK and Spring MVC. Therefore, the design of shopping cart may not be the best way.

Setup the sample project

In this article, we use Eclipse with m2eclipse plugin to create a new ZK Maven project. Please refer to the installation guide here for more detail.

  • First, we need to add Spring MVC dependency in the pom.xml file.
<!-- Spring MVC dependency -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-webmvc</artifactId>
	<version>3.1.2.RELEASE</version>
</dependency>
<!-- ZK dependency -->
<!-- ommitted -->
  • Then, define Spring MVC DispatherServlet in web.xml file.
<!-- Spring MVC servlet -->
<servlet>
	<servlet-name>springmvc</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>springmvc</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- ZK servlet -->
<!-- ommitted -->
  • Finally, create a [servlet-name]-servlet.xml file under WEB-INF folder. Note that the file name pattern must match the servlet name defined in web.xml, For this example, the file name is springmvc-servlet.xml. We can define the ViewResolver in this file.
<beans ...>
	<mvc:annotation-driven />
	<mvc:resources location="/images/" mapping="/img/**" />
	
	<context:component-scan base-package="demo.controller.springmvc" />
	<context:component-scan base-package="demo.data.service" />
	
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/" />
		<property name="suffix" value="" />
	</bean>
</beans>

Project Structure

Zkspringmvc-project.png

  • package demo.controller.springmvc
    • FormController.java - put all form action submit here.
    • PageFlowController.java - define the page flows here.
  • package demo.view.zk
    • CartViewModel.java - the cart view model used in cart.zul file.
    • ProductViewModel.java - the product view model used in product.zul file.

Communicate between Spring MVC and ZK

The scenario we are trying to do is like this. After the user logged in, we dispatch to a zul page using Spring MVC controller class. In this zul page, ZK could perform adding product to shopping cart easily without complex form submit procedure. Finally, we can pass the items in shopping cart to Spring MVC controller class with a single form submit operation.

Spring MVC to ZK MVVM

First, if user logged in successfully, we will redirect the path to shop (use redirect instead of forward to avoid duplicate form submission problem when user refresh browser page). Here is the code snippet of PageFlowController.java that handles the request.

@Controller
@RequestMapping("/shopping")
public class PageFlowController {
	@Autowired
	private ProductDAO prodDao;

	@RequestMapping(value = "/shop", method = RequestMethod.GET)
	public String shop(ModelMap model, HttpSession session) {
		if (isLogged(session)) {
			model.addAttribute("productList", prodDao.findAll());
			return "zul/shopping.zul";
		}
		return "redirect:index";
	}
}

Here you can see in line 10, the product list has been stored in request scope, and will forward the request to shopping.zul page which include product.zul and cart.zul in line 11.

<!-- shopping.zul -->
<?page title="Shopping Cart Sample - Purchase" ?>
<zk>
	<borderlayout>
		<center title="Product List">
			<include hflex="1" src="product.zul" />
		</center>
		<south size="35%" title="Shopping Cart">
			<include hflex="1" src="cart.zul" />
		</south>
	</borderlayout>
</zk>
<!-- product.zul -->
<zk>
	<grid model="@bind(vm.productList)" vflex="1" 
		apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('demo.view.zk.ProductViewModel')">
		<columns>
			<column width="212px" />
			<column label="Name" />
			<column label="Price" />
			<column label="Add to Cart" />
		</columns>
		<template name="model">
			<row>
				<image src="/img/noimage.png" />
				<label value="@load(each.name)" />
				<label value="@load(each.price) @converter('formattedNumber', format='$ #,###,###')" />
				<cell>
					<spinner value="@bind(each.quantity)" constraint="no negative" />
					<button label="Add" onClick="@global-command('addCart', product=each)" />
				</cell>
			</row>
		</template>
	</grid>
</zk>

In product.zul, we can display product list with MVVM pattern. Here is how we get the product list in ProductViewModel.

public class ProductViewModel {
	
	private List<Product> productList;
	
	@Init
	public void init(@ExecutionParam("productList") List<Product> productList) {
		this.productList = productList;
	}
	
	public List<Product> getProductList() {
		return productList;
	}
}

Since the product is stored in request scope, we can retrieve the data from ZK Execution scope in line 6. Now, we have successfully passed the required data from Spring MVC to ZK MVVM.

ZK MVVM to Spring MVC

In the previous section, we can show product list from Spring MVC. In this section we will describe how to send shopping cart items back to Spring MVC for further operation.

Now, take a look at cart.zul. We need to use native form tag (line 3) for sending request (line 34) back to Spring MVC controller.

<zk xmlns:n="native">
	<window apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('demo.view.zk.CartViewModel')">
		<n:form action="confirmOrder" method="post">
			<grid model="@bind(vm.cartList)">
				<columns>
					<column label="Name" />
					<column label="Price" />
					<column label="Quantity" />
					<column label="Sub total" />
					<column label="id" visible="false" />
					<column label="prodId" visible="false" />
					<column label="Name" visible="false" />
					<column label="Price" visible="false" />
					<column label="Quantity" visible="false" />
				</columns>
				<template name="model">
					<row>
						<label value="@bind(each.product.name)" />
						<label value="@bind(each.product.price) @converter('formattedNumber', format='$ #,###,###')" />
						<label value="@bind(each.quantity)" />
						<label value="@bind(each.subtotal) @converter('formattedNumber', format='$ #,###,###')" />
						<textbox name="items[${forEachStatus.index}].id" value="${forEachStatus.index}" />
						<textbox name="items[${forEachStatus.index}].prodId" value="@bind(each.product.id)" />
						<textbox name="items[${forEachStatus.index}].name" value="@bind(each.product.name)" />
						<textbox name="items[${forEachStatus.index}].price" value="@bind(each.product.price)" />
						<textbox name="items[${forEachStatus.index}].quantity" value="@bind(each.quantity)" />
					</row>
				</template>
				<foot>
					<footer span="3">Total</footer>
					<footer>
						<hlayout valign="middle">
							<label value="@bind(vm.total) @converter('formattedNumber', format='$ #,###,###')" />
							<button label="Confirm Order" type="submit" disabled="@bind(empty vm.cartList)" autodisable="self" />
						</hlayout>
					</footer>
				</foot>
			</grid>
		</n:form>
	</window>
</zk>

Notice that line 10-14 and line 22-26 are invisible columns which are used to bind collection form data. And in FormController.java, we can get data easily as follows:

@Controller
@RequestMapping("/shopping")
public class FormController {
	@Autowired
	private OrderDAO orderDao;

	@RequestMapping(value = "/confirmOrder", method = RequestMethod.POST)
	public String confirmForm(@ModelAttribute Order order, HttpSession session) {
		if (session.getAttribute("logged") != null) {
			int id = orderDao.findMaxId();
			order.setId(id);
			orderDao.saveOrder(order);
			return "redirect:confirm/" + id;
		}
		return "redirect:index";
	}
}

In line 8, we can see that by attaching the annotation @ModelAttribute, Spring MVC will automatically bind the form data to Order bean which contains a OrderItem list data.

public class Order {
	private int id;
	private List<OrderItem> items = new ArrayList<OrderItem>();

	// getters and setters
}

Conclusion

In the example above, we can see how ZK communicates with Spring MVC easily. With ZK's help, we can reduce page flow maintenance cost.

Download

You can check out the source code from github.


Comments



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.