Hello, Thanks for your reply. I have created prototype pivot application which contains KpiTableView (table of KPI items), KpiFormView (editor of KPI properties) and MainView (contains cardpane for switching between table and form). When you double click the table, you can edit selected item in form.
There is commom vew model KpiViewModel and controller KpiController. I use view model to hold data to be displayed in Pivot views. The controller implements presentation logic - manipulates the model, calls remote services. The changes in the model are propagated to view using data binding. For example - if I call setData on model, I want the Pivot TableView automatically update with new data. The goal is to separate data, presentation logic and view (GUI definition) and also to make controller class clean and junit testable. The prefered way to update the view from controller method is to update the view model. The view detects change in model and automatically updates GUI based on data binding. There still must be option to access view from controller via View interface, which is not the prefered way. It adds dependency of controller on view interface and the view interface needs to be mocked for junit - see editData() in KpiController. My approach is strongly inspired by http://corlan.org/bootcamp/MicroArchitecture2.pdf You can find complete sources of my sample here: https://docs.google.com/open?id=0B2tRTRe1CWKUQVZuYkVoTTZpT3M Here is some code: public class KpiController { private KpiViewModel viewModel=BeanRegistry.getInstance().getKpiViewModel(); public void loadData() { KpiService service=new KpiService(); viewModel.setData(service.getNewData()); } public void editData() { KpiForm form=BeanRegistry.getInstance().getKpiForm(); viewModel.setEditedItem(viewModel.getSelectedItem()); form.loadData(); showForm(); } public void save() { KpiService service=new KpiService(); service.merge(viewModel.getEditedItem()); showTable(); } public void showTable(){ viewModel.setSelectedCardIndex(KpiViewModel.KPI_TABLE_VIEW); } public void showForm(){ viewModel.setSelectedCardIndex(KpiViewModel.KPI_FORM_VIEW); } } public class KpiViewModel { public static final int KPI_FORM_VIEW=1; public static final int KPI_TABLE_VIEW=0; private ArrayList<KpiVO> data = new ArrayList<KpiVO>(); private KpiVO selectedItem; private KpiVO editedItem; private int selectedCardIndex=KPI_TABLE_VIEW; private KpiViewModelListenerList listenerList=new KpiViewModelListenerList(); public KpiVO getSelectedItem() { return selectedItem; } public void setSelectedItem(KpiVO selectedKpiVO) { this.selectedItem = selectedKpiVO; } public ArrayList<KpiVO> getData() { return data; } public void setData(ArrayList<KpiVO> data) { this.data = data; listenerList.dataChanged(this); } public KpiVO getEditedItem() { return editedItem; } public void setEditedItem(KpiVO editedItem) { this.editedItem = editedItem; } public int getSelectedCardIndex() { return selectedCardIndex; } public void setSelectedCardIndex(int selectedCardIndex) { this.selectedCardIndex = selectedCardIndex; listenerList.selectedCardIndexChanged(this); } public ListenerList<KpiViewModelListener> getListenerList() { return listenerList; } /** * Model listener interface. * */ public interface KpiViewModelListener { public void dataChanged(Object bean); public void selectedCardIndexChanged(Object bean); public void selectedItemChanged(Object bean); } private class KpiViewModelListenerList extends ListenerList<KpiViewModelListener> implements KpiViewModelListener { public void dataChanged(Object bean) { for (KpiViewModelListener l : this) { l.dataChanged(bean); } } public void selectedCardIndexChanged(Object bean) { for (KpiViewModelListener l : this) { l.selectedCardIndexChanged(bean); } } public void selectedItemChanged(Object bean) { for (KpiViewModelListener l : this) { l.selectedItemChanged(bean); } } } } public class KpiControllerTest extends TestCase { private BeanRegistry beanRegistry=BeanRegistry.getInstance(); private KpiController controller; private KpiViewModel model; protected void setUp() throws Exception { super.setUp(); if (beanRegistry.getKpiForm()==null) { BeanRegistry.getInstance().registerKpiForm(new KpiFormMock()); } controller=new KpiController(); model=beanRegistry.getKpiViewModel(); } public void testLoadData() { controller.loadData(); assertTrue(model.getData().getLength()>0); } public void testEditData() { model.setSelectedItem(model.getData().get(0)); controller.editData(); assertNotNull(model.getEditedItem()); } public void testSave() { controller.save(); } public void testShowTable(){ controller.showTable(); assertEquals(model.getSelectedCardIndex(), KpiViewModel.KPI_TABLE_VIEW); } public void testShowForm(){ controller.showForm(); assertEquals(model.getSelectedCardIndex(), KpiViewModel.KPI_FORM_VIEW); } public static class KpiFormMock implements KpiForm { public void loadData() {} } } public class KpiTableView extends CardPane implements Bindable { private Map<String, Object> namespace; private KpiController controller=new KpiController(); @BXML private TableView kpiTable; @BXML private PushButton loadButton; @Override public void initialize(Map<String, Object> namespace, URL arg1, Resources arg2) { this.namespace=namespace; addBindings(); registerListeners(); controller.loadData(); } private void addBindings() { new NamespaceBinding(this.namespace,"kpiTable.selectedRow", "model.selectedItem").bind(); new NamespaceBinding(this.namespace,"model.selectedItem", "kpiTable.selectedRow").bind(); } private void registerListeners() { kpiTable.getComponentMouseButtonListeners().add(new KPITableMouseButtonListener()); loadButton.getButtonPressListeners().add(new LoadButtonListener()); } private class KPITableMouseButtonListener extends ComponentMouseButtonAdapter{ @Override public boolean mouseDoubleClick() { controller.editData(); return true; } } private class LoadButtonListener implements ButtonPressListener { @Override public void buttonPressed(org.apache.pivot.wtk.Button button) { controller.loadData(); } } } KpiTableView.bxml <view:KpiTableView bxml:id="view" xmlns:bxml="http://pivot.apache.org/bxml" xmlns:collections="org.apache.pivot.collections" xmlns="org.apache.pivot.wtk" xmlns:view="com.gemsystem.kpimanager.view" xmlns:model="com.gemsystem.kpimanager.model" > <bxml:script> var model=com.gemsystem.kpimanager.BeanRegistry.getInstance().getKpiViewModel(); </bxml:script> <FillPane> <BoxPane orientation="vertical"> <Label text="KPI List" /> <Border> <ScrollPane> <TableView bxml:id="kpiTable" tableData="${model.data}"> <columns> <TableView.Column name="title" width="180" headerData="Name"/> <TableView.Column name="amount" width="150" headerData="Amount"/> <TableView.Column name="description" width="350" headerData="Description"/> </columns> </TableView> <columnHeader> <TableViewHeader tableView="$kpiTable"/> </columnHeader> </ScrollPane> </Border> <BoxPane styles="{padding:4, verticalAlignment:'center'}"> <PushButton bxml:id="loadButton" buttonData="Load data"/> </BoxPane> </BoxPane> </FillPane> </view:KpiTableView> Regards Karel -----Original Message----- From: Roger Whitcomb [mailto:[email protected]] Sent: Tuesday, December 11, 2012 3:09 PM To: [email protected] Subject: Re: MVC - Binding POJO model classes to Pivot View Hi Karel, Thanks for using Pivot. I'm wondering how exactly you are using your "MyModel" class? Because what it seems like you are describing is exactly what the Pivot Collections classes already do. So, for instance, if you want to display a list of objects in a TableView, if you were to use a Pivot ArrayList<MyObject> and set that as the data (TableView.setData(new ArrayList<MyObject>()) then the TableView would automatically be notified when changes are made to the list and the view would be updated. There is not usually any need to implement these listeners yourself since the Pivot collections classes already do that (which is in fact the reason they exist at all is to provide these notifications to the view components). So, maybe you could provide a little more code so we can see exactly how you are using your model class. But, I'm thinking that much of what you are describing could be done more simply. HTH, ~Roger Whitcomb
