Introduction

In the previous post, we have explored the benefits of combining ZK with Spring MVC. In essence, applying the power of Spring MVC to ZK UI framework allows seamless communication between client and server channels, especially since ZK framework will handle the routine tasks, such as HTML DOM update, Browser compatibility, and JavaScript code debugging. In this article, we will go into more detail about manipulating the data between Spring MVC and ZK MVVM data binding.

Book Management Demo (CRUD)

The following demonstrates leveraging Spring MVC controller with ZK UI components (MVVM way) to develop a Book management system with CRUD operations in Java code. And it uses ZK Form Binding to cache the temporary value in a proxy object and then saves to the original object after a user clicked the “save” button.

Note: ZK MVVM is just one of the options. You can use ZK MVC or ZK 8 shadow element as well.


Get Adobe Flash player

Required Configuration

Versions:

  • Spring MVC: 3.1 or later
  • ZK CE: 8.0.1.FL.20151113 or later
  • ZK PE and EE: (Optional)

web.xml


    
        springmvc
        org.springframework.web.servlet.DispatcherServlet
        
        1
    
    
        springmvc
        /
    

springmvc-servlet.xml

    
    <context:annotation-config/>
    <bean class="org.zkoss.zkspringmvc.config.ZKWebMvcConfigurerAdapter"/>
    
    
    <context:component-scan base-package="org.zkoss.zkspringmvc.demo" />

    
    
        <property name="viewClass" value="org.zkoss.zkspringmvc.ZKView"/>
        <property name="prefix" value="/WEB-INF/" />
        <property name="suffix" value="" />
    

Defining a Controller

We provided a few new annotations for developers to use.

  • @ZKSelector: [ElementType.PARAMETER] a way to query ZK component or input value with ZK CSS-like Selector
  • @ZKVariable: [ElementType.PARAMETER] a way to evaluate ZK variable (such as EL variable in ZK scope)
  • @ZKCommandLifecyle: [ElementType.METHOD] a way to trigger the ZK MVVM command lifecycle during invoking the associated method
  • @ZKNotifyChange: [ElementType.METHOD] a way to trigger ZK MVVM’s notify change mechanism

BooksController.java

@Controller
@RequestMapping("/mvvm/books")
@SessionAttributes({"booksVM"})
public class BooksController {
	@RequestMapping(value = {"", "/"}, method = RequestMethod.GET)
	public String index(ModelMap model) {
		String[][] BOOKS = {{"The Very Hungry Caterpillar", "Eric Carle",
				"Children,Classics,Animals"},
				{"The New Way Things Work", "David Macaulay",
						"Education,Science,Computers"},
				{"The DASH Diet Younger You", "Marla Heller",
						"Health,Fitness,Diets"}};
		BooksVM books = new BooksVM();
		books.setBooks(init(BOOKS));
		books.setCurrentBook(books.getBooks().get(0));

		model.addAttribute(books);
		return "mvvm/index.zul";
	}

	private LinkedList<Book> init(String[][] BOOKS) {
		//omitted
	}

	private Book initBook(String name, String author, String categories) {
		//omitted
	}

	@RequestMapping(value = "/addCate", method = RequestMethod.POST)
	@ZKCommandLifecycle
	public String doAddCategory(@ZKVariable Book fx,
			@ZKSelector String cateName) {
		Set<Category> categories = (Set<Category>) fx.getCategories();
		categories.add(new Category(cateName));
		return ZKModelAndView.SELF;
	}

	@RequestMapping(value = "/removeCate", method = RequestMethod.POST)
	public String doRemoveCategory(@ZKVariable Book fx,
			@ZKVariable Category each) {
		Set<Category> categories = (Set<Category>) fx.getCategories();
		categories.remove(each);
		return ZKModelAndView.SELF;
	}

	@RequestMapping(value = "/reset", method = RequestMethod.POST)
	@ZKCommandLifecycle
	public @ZKNotifyChange({"vm.currentBook"})
	ZKModelAndView onReset(@ModelAttribute BooksVM booksVM) {
		ZKModelAndView model = new ZKModelAndView();
		model.addObject("vm", booksVM);
		return model;
	}

	@RequestMapping(value = "/save", method = RequestMethod.POST)
	@ZKCommandLifecycle
	public @ZKNotifyChange({"editable", "currentBook"})
	BooksVM onSave(@ModelAttribute BooksVM booksVM) {
		booksVM.setEditable(false);
		return booksVM;
	}

	@RequestMapping(value = "/edit", method = RequestMethod.POST)
	public @ZKNotifyChange("editable")
	BooksVM edit(@ModelAttribute BooksVM booksVM) {
		booksVM.setEditable(true);
		return booksVM;
	}
}

The following explains the highlights from above one by one.

  1. Line 5, 6: a Spring MVC request mapping to a index() method, and then we prepare the Books data in the ModelMap object that Spring MVC provided.
  2. Line 18: returning the View as a normal ZK page “mvvm/index.zul”. Note: you can use “redirect:foo” or “forward:foo” as well.
  3. Line 30: an annotation to trigger ZK MVVM command lifecycle. If the value is empty, it will use @Requestmapping‘s first value. (i.e. “/addCate” in this case)
  4. Line 31: an annotation to lookup the Book object from the same variable name “fx” in ZK EL scope.
  5. Line 32: an annotation to query the input value from the component that matches with the ID “cateName”
  6. Line 35: returning the ZK SELF view, it means “Please handle the response in a ZK update way”
  7. Line 48, 52: an annotation to trigger ZK MVVM’s notify change mechanism based on the returned model, it will find the base object which has the same key with “vm” in the model to notify the change of its value “currentBook”, and then update all data, depending on the variable in the zul page, automatically (it’s the ZK MVVM power)
  8. Line 57, 60: the same way to trigger the notify change with line 48 and 52, if the returned value is not a kind of ZKModelAndView, it will use it as the base object to notify.
  9. Line 58: an annotation to receive the request or session attributes that SpringMVC provided.

Java Bean (POJO)

BooksVM.java

public class BooksVM {
	private List<Book> books;
	private Book currentBook;
	private boolean editable;
	//omitted getter and setter
}

Book.java

public class Book {
	private Set<Category> categories = new LinkedHashSet<Category>();
	private String author;
	private String name;
	//omitted getter and setter
}

Category.java

public class Category {
	private String name;
	//omitted getter and setter
}

Preparing a View

The ZK View can be a Zul, Xhtml, html, or combined with JSP.
In this demo we have chosen a Zul page with some parts of Xhtml tags, and specified some namespace declarations within line 1 in the index.zul file.

  • xmlns:n=”native”: a pure html tag without Java object
  • xmlns:x=”xhtml”: a ZK html tag that can be used from Java side
  • xmlns:ca=”client/attribute”: a pure html data attribute that is used to declare the action of SpringMVC in this case, such as “ca:data-springmvc-action=’/save'”

index.zul

As the index.zul is quite long, we have separated it into four parts to explain in detail.


    
    
        

            

            
        
    

As you can see from above, in line 5 we declare an ID “vm” and pass a variable “booksVM” into it, the “booksVM” can be found in the BooksController#index() method that we prepared the book data and then pass it into the ModelMap object.

index.zul Part I (List view)

In line 1, it loads the book data from the variable “vm.books” and then auto-bind the “vm.currentBook” into the selected item.

And in line 12, we used ZK Children Binding mechanism to iterate the collection and render the template one by one, you can replace it by using ZK 8 shadow element as well.


    
        
        
        
    
    

index.zul Part 2 (Grid form submit)

The Grid form submit layout provides two ways for showing: one is editable view and the other is read-only view, the displayed view depends on the variable of “vm.editable” in line 13.

And line 1 declares the form with an ID variable “fx”, which is a ZK MVVM Form Binding, and then it will load the “vm.currentBook” to the “fx” proxy object, which is used to cache all the changes into the proxy object, once the command “/save” is triggered, the ZK MVVM Binder will save the caches from the proxy object to the “vm.currentBook”. (The trigger command with “/save”, you can find from BooksController.java line 56 with “@ZKCommandLifecycle”.

And the lines 4~6 are the buttons to trigger the SpringMVC actions, “edit”, “reset”, and “save” from the declaration “ca:data-springmvc-action”. If “ca:data-springmvc-trigger” is not specified, it assumes “onClick” event is the trigger event.


    
        
        
        
        
    
    
        
            
            
        
        
            
            
        
    

index.zul Part 3 (Read-only view)


    Name:
    

    Author:
    

    Categories:
    
        
    

index.zul Part 4 (Editable view)

Lines 18 and 27 are the buttons to trigger “addCate” and “removeCate” Spring MVC actions.

Line 26 is the input value with ID “cateName” which is used with “@ZKSelector String cateName” in BooksController.java line 32, when “addCate” action is triggered.


    Name:
        


    Author:
        


    Categories:
    
        
            
        
        
            Add Category
            
            
        
    

Now, you can run the code above with Tomcat or Jetty server. Please visit “http://localhost:8080/zkspringmvc-demo/mvvm/books/” to view the demo.

Downloads

The whole demo project can be found at Github project

References:

If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.

4 Responses to “Rich Web Application with Spring MVC CRUD Demo – Part II”

  1. Trong Dinh says:

    Nice demo but I had to use ca:data-springmvc-action=”books/[action]” due to “no mapping for mvvm/[action]” with jetty & tomcat run

  2. Jumper Chen says:

    @Trong,
    the ca:data-sprinngmvc-action is used to trigger SpringMVC’s request mapping. However, you can still use the @command with a button to trigger for MVVM way, it’s possible too.

  3. […] the previous two articles Rich Web Application with Spring MVC CRUD Demo and  Rich Web Application with Spring MVC CRUD Demo – Part II, we’ve explored the advantages of combining ZK with Spring MVC and the benefits of […]

  4. Admilson cossa says:

    Well I have a question about form validation.

    The validation is the first phase in command execution life-cycle. it means if there is any error in form it breaks the execution and the next steps are not performed.

    But in this case using ca:data-springmvc-action and @ZKCommandLifecycle , when we perform a validation the next steps in the life-cyle are executed even with errors in the form causing it to run the needed method with an invalid form.

    So my question is how to make it stop or work like the normal way it works using ca:data-springmvc-action?

Leave a Reply