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


Reply via email to