This demo shows the 2nd method: using the Range API to save the cell data back to the database and publish a table's data to cells. The architecture is as follows: :
<zk xmlns="http://www.zkoss.org/2005/zul"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.zkoss.org/2005/zul http://keikai.io/2019/zul/zul.xsd">
<spreadsheet id="spreadsheet" width="100%" height="100%" apply="demo.database.BudgetComposer"
maxVisibleRows="35" maxVisibleColumns="11" hiderowhead="true" hidecolumnhead="true"
src="/WEB-INF/books/budget.xlsx"/>
</zk>package demo.database;
import demo.Util;
import io.keikai.api.*;
import io.keikai.ui.Spreadsheet;
import io.keikai.ui.event.*;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.*;
import java.util.List;
public class BudgetComposer extends SelectorComposer<Component> {
@Wire
protected Spreadsheet spreadsheet;
protected ExpenseDao expenseDao = new ExpenseDao();
//sheet name
protected static final String SUMMARY = "summary";
protected static final String NEW = "new";
//named range
protected static final String QUANTITY = "QUANTITY";
protected static final String CATEGORY = "CATEGORY";
protected static final SheetProtection ALLOW_SELECT = SheetProtection.Builder.create().withSelectLockedCellsAllowed(true).withAutoFilterAllowed(true).build();
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
loadExpenseToSheet();
Ranges.range(spreadsheet.getSelectedSheet()).protectSheet(ALLOW_SELECT);
}
protected void loadExpenseToSheet() {
List<Expense> expenseList = expenseDao.queryAll();
fillExpenses(expenseList);
}
protected void fillExpenses(List<Expense> list) {
Range quantity1stCell = Ranges.rangeByName(spreadsheet.getBook().getSheet(SUMMARY), QUANTITY);
for (int i = 0; i < list.size(); i++) {
Expense expense = list.get(i);
Ranges.range(spreadsheet.getSelectedSheet(), quantity1stCell.getRow() + i, quantity1stCell.getColumn()).setCellValue(expense.getQuantity());
Ranges.range(spreadsheet.getSelectedSheet(), quantity1stCell.getRow() + i, quantity1stCell.getColumn() + 1).setCellValue(expense.getSubtotal());
}
}
@Listen(Events.ON_CELL_CLICK + "= #spreadsheet")
public void onClick(CellMouseEvent event) {
Range cell = Util.getClickedCell(event);
if ("Add new".equals(cell.getCellValue())){
spreadsheet.setSelectedSheet(NEW);
}else if ("Done".equals(cell.getCellValue())){
readExpense();
spreadsheet.setSelectedSheet(SUMMARY);
loadExpenseToSheet();
}
}
/**
* read input expense list in a specific range into the database
*/
protected void readExpense() {
Range category1stCell = Ranges.rangeByName(spreadsheet.getSelectedSheet(), CATEGORY);
int startingRow = category1stCell.getRow();
int categoryColumn = category1stCell.getColumn();
for (int rowIndex = startingRow; rowIndex < startingRow + 4; rowIndex++) {
Expense expense = readExpense(rowIndex, categoryColumn);
if (!validate(expense)) {
break; //don't read the next row
}
expenseDao.update(expense);
}
Ranges.range(spreadsheet.getSelectedSheet(), startingRow, 0, startingRow+3, 3).clearContents();
}
protected boolean validate(Expense expense) {
return expense.getCategory() != null
&& expense.getQuantity() > 0
&& expense.getSubtotal() > 0;
}
protected Expense readExpense(int row, int col) {
Expense expense = new Expense();
expense.setCategory(Ranges.range(spreadsheet.getSelectedSheet(), row, col).getCellData().getStringValue());
Double value = Ranges.range(spreadsheet.getSelectedSheet(), row, col+1).getCellData().getDoubleValue();
expense.setQuantity(value == null? 0 : value.intValue());
value = Ranges.range(spreadsheet.getSelectedSheet(), row, col+3).getCellData().getDoubleValue();
expense.setSubtotal(value == null? 0 : value.intValue());
return expense;
}
}
package demo.database;
import java.util.*;
/**
* Data Access Object (DAO), simulates a persistence layer to access a simulated database
*/
public class ExpenseDao {
private List<Expense> expenseTable = new LinkedList<>(); //simulated database table
private int id = 0;
//sample expenses
public ExpenseDao() {
Expense expense1 = new Expense();
expense1.setCategory("Transportation");
expense1.setQuantity(10);
expense1.setSubtotal(2400);
expenseTable.add(expense1);
Expense expense2 = new Expense();
expense2.setCategory("Food");
expense2.setQuantity(16);
expense2.setSubtotal(15);
expenseTable.add(expense2);
Expense expense3 = new Expense();
expense3.setCategory("Lodging");
expense3.setQuantity(12);
expense3.setSubtotal(160);
expenseTable.add(expense3);
Expense expense4 = new Expense();
expense4.setCategory("Entertainment");
expense4.setQuantity(11);
expense4.setSubtotal(120);
expenseTable.add(expense4);
}
public List<Expense> queryAll() {
List result = new LinkedList();
result.addAll(expenseTable);
return Collections.unmodifiableList(result);
}
public void update(Expense newExpense) {
Expense expense = find(newExpense);
if (expense != null) {
expense.setQuantity(expense.getQuantity() + newExpense.getQuantity());
expense.setSubtotal(expense.getSubtotal() + newExpense.getSubtotal());
}
}
/**
* @param newExpense
* @return a dummy object or found expense
*/
public Expense find(Expense newExpense) {
Expense target = null; //dummy object
for (int i = 0; i < expenseTable.size(); i++) {
target = expenseTable.get(i);
if (newExpense.getCategory().equals(target.getCategory())) {
break;
}
}
return target;
}
}
package demo.database;
public class Expense {
private int id;
private String category;
private int quantity;
private int subtotal;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int getSubtotal() {
return subtotal;
}
public void setSubtotal(int subtotal) {
this.subtotal = subtotal;
}
@Override
public String toString() {
return id+category+quantity+subtotal;
}
}